minitest-heat 0.0.9 → 0.0.13

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.
@@ -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
 
@@ -12,16 +13,36 @@ module Minitest
12
13
  end
13
14
 
14
15
  def tokens
15
- map.file_hits.each do |hit|
16
- file_tokens = pathname(hit)
17
- line_number_tokens = line_numbers(hit)
16
+ results.heat_map.file_hits.each do |hit|
17
+ # Focus on the relevant issues based on most significant problems. i.e. If there are
18
+ # legitimate failures or errors, skips and slows aren't relevant
19
+ next unless relevant_issue_types?(hit)
18
20
 
19
- next if line_number_tokens.empty?
21
+ # Add a new line
22
+ @tokens << [[:muted, ""]]
20
23
 
21
- @tokens << [
22
- *file_tokens,
23
- *line_number_tokens
24
- ]
24
+ # Build the summary line for the file
25
+ @tokens << file_summary_tokens(hit)
26
+
27
+ # Get the set of line numbers that appear more than once
28
+ repeated_line_numbers = find_repeated_line_numbers_in(hit)
29
+
30
+ # Only display more details if the same line number shows up more than once
31
+ next unless repeated_line_numbers.any?
32
+
33
+ repeated_line_numbers.each do |line_number|
34
+ # Get the backtraces for the given line numbers
35
+ traces = hit.lines[line_number.to_s]
36
+
37
+ # If there aren't any traces there's no way to provide additional details
38
+ break unless traces.any?
39
+
40
+ # A short summary explaining the details that will follow
41
+ @tokens << [[:muted, " Issues on Line #{line_number} initially triggered from these locations:"]]
42
+
43
+ # The last relevant location for each error's backtrace
44
+ @tokens += origination_sources(traces)
45
+ end
25
46
  end
26
47
 
27
48
  @tokens
@@ -29,13 +50,41 @@ module Minitest
29
50
 
30
51
  private
31
52
 
32
- def map
33
- results.heat_map
53
+ def origination_sources(traces)
54
+ # 1. Sort the traces by the most recent line number so they're displayed in numeric order
55
+ # 2. Get the final relevant location from the trace
56
+ traces.
57
+ sort_by { |trace| trace.locations.last.line_number }.
58
+ map { |trace| origination_location_token(trace) }
59
+ end
60
+
61
+ def file_summary_tokens(hit)
62
+ pathname_tokens = pathname(hit)
63
+ line_number_list_tokens = sorted_line_number_list(hit)
64
+
65
+ [*pathname_tokens, *line_number_list_tokens]
66
+ end
67
+
68
+ def origination_location_token(trace)
69
+ # The earliest project line from the backtrace—this is probabyl wholly incorrect in terms
70
+ # of what would be the most helpful line to display, but it's a start.
71
+ location = trace.locations.last
72
+
73
+ [
74
+ [:muted, " #{Output::SYMBOLS[:arrow]} "],
75
+ [:default, location.relative_filename],
76
+ [:muted, ':'],
77
+ [:default, location.line_number],
78
+ [:muted, " in #{location.container}"],
79
+ [:muted, " #{Output::SYMBOLS[:arrow]} `#{location.source_code.line.strip}`"],
80
+ ]
34
81
  end
35
82
 
36
83
  def relevant_issue_types
84
+ # These are always relevant.
37
85
  issue_types = %i[error broken failure]
38
86
 
87
+ # These are only relevant if there aren't more serious isues.
39
88
  issue_types << :skipped unless results.problems?
40
89
  issue_types << :painful unless results.problems? || results.skips.any?
41
90
  issue_types << :slow unless results.problems? || results.skips.any?
@@ -43,32 +92,85 @@ module Minitest
43
92
  issue_types
44
93
  end
45
94
 
46
- def pathname(file)
47
- directory = "#{file.pathname.dirname.to_s.delete_prefix(Dir.pwd)}/"
48
- filename = file.pathname.basename.to_s
95
+ def relevant_issue_types?(hit)
96
+ # The intersection of which issue types are relevant based on the context and which issues
97
+ # matc those issue types
98
+ intersection_issue_types = relevant_issue_types & hit.issues.keys
99
+
100
+ intersection_issue_types.any?
101
+ end
102
+
103
+ def find_repeated_line_numbers_in(hit)
104
+ repeated_line_numbers = []
105
+
106
+ hit.lines.each_pair do |line_number, traces|
107
+ # If there aren't multiple traces for a line number, it's not a repeat
108
+ next unless traces.size > 1
109
+
110
+ repeated_line_numbers << Integer(line_number)
111
+ end
112
+
113
+ repeated_line_numbers.sort
114
+ end
115
+
116
+ def pathname(hit)
117
+ directory = hit.pathname.dirname.to_s.delete_prefix("#{Dir.pwd}/")
118
+ filename = hit.pathname.basename.to_s
49
119
 
50
120
  [
51
- [:default, directory],
121
+ [:default, "#{directory}/"],
52
122
  [:bold, filename],
53
123
  [:default, ' · ']
54
124
  ]
55
125
  end
56
126
 
57
- def hit_line_numbers(file, issue_type)
58
- numbers = []
59
- line_numbers_for_issue_type = file.issues.fetch(issue_type) { [] }
60
- line_numbers_for_issue_type.sort.map do |line_number|
61
- numbers << [issue_type, "#{line_number} "]
127
+ # Gets the list of line numbers for a given hit location (i.e. file) so they can be
128
+ # displayed after the file name to show which lines were problematic
129
+ # @param hit [Hit] the instance to extract line numbers from
130
+ #
131
+ # @return [Array<Symbol,String>] [description]
132
+ def line_number_tokens_for_hit(hit)
133
+ line_number_tokens = []
134
+
135
+ relevant_issue_types.each do |issue_type|
136
+ # Retrieve any line numbers for the issue type
137
+ line_numbers_for_issue_type = hit.issues.fetch(issue_type) { [] }
138
+
139
+ # Build the list of tokens representing styled line numbers
140
+ line_numbers_for_issue_type.each do |line_number|
141
+ line_number_tokens << line_number_token(issue_type, line_number)
142
+ end
62
143
  end
63
- numbers
144
+
145
+ line_number_tokens.compact
64
146
  end
65
147
 
66
- def line_numbers(file)
67
- line_number_tokens = []
68
- relevant_issue_types.each do |issue_type|
69
- line_number_tokens += hit_line_numbers(file, issue_type)
148
+ # Builds a token representing a styled line number
149
+ #
150
+ # @param style [Symbol] the relevant display style for the issue
151
+ # @param line_number [Integer] the affected line number
152
+ #
153
+ # @return [Array<Symbol,Integer>] array token representing the line number and issue type
154
+ def line_number_token(style, line_number)
155
+ [style, "#{line_number} "]
156
+ end
157
+
158
+ # Sorts line number tokens so that line numbers are displayed in order regardless of their
159
+ # underlying issue type
160
+ #
161
+ # @param hit [Hit] the instance of the hit file details to build the heat map entry
162
+ #
163
+ # @return [Array] the arrays representing the line number tokens to display next to a file
164
+ # name in the heat map. ex [[:error, 12], [:falure, 13]]
165
+ def sorted_line_number_list(hit)
166
+ # Sort the collected group of line number hits so they're in order
167
+ line_number_tokens_for_hit(hit).sort do |a, b|
168
+ # Ensure the line numbers are integers for sorting (otherwise '100' comes before '12')
169
+ first_line_number = Integer(a[1].strip)
170
+ second_line_number = Integer(b[1].strip)
171
+
172
+ first_line_number <=> second_line_number
70
173
  end
71
- line_number_tokens.compact.sort_by { |number_token| number_token[1] }
72
174
  end
73
175
  end
74
176
  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, "#{pluralize(timer.test_count, 'test')}"]
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, "#{pluralize(timer.assertion_count, 'assertion')}"]
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
- # Builds the collection of tokens representing a specific set of source code lines
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
 
@@ -11,18 +11,18 @@ require_relative 'output/token'
11
11
  module Minitest
12
12
  module Heat
13
13
  # Friendly API for printing nicely-formatted output to the console
14
- class Output
14
+ class Output # rubocop:disable Metrics/ClassLength
15
15
  SYMBOLS = {
16
16
  middot: '·',
17
17
  arrow: '➜',
18
- lead: '|',
18
+ lead: '|'
19
19
  }.freeze
20
20
 
21
21
  TOKENS = {
22
- spacer: [:muted, " #{SYMBOLS[:middot]} "],
22
+ spacer: [:muted, " #{SYMBOLS[:middot]} "],
23
23
  muted_arrow: [:muted, " #{SYMBOLS[:arrow]} "],
24
- muted_lead: [:muted, "#{SYMBOLS[:lead]} "],
25
- }
24
+ muted_lead: [:muted, "#{SYMBOLS[:lead]} "]
25
+ }.freeze
26
26
 
27
27
  attr_reader :stream
28
28
 
@@ -42,8 +42,33 @@ 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
+ #
53
+ # This way, as you fix issues, the list gets shorter, and eventually the least critical
54
+ # issues will be displayed without scrolling once more problematic issues are resolved.
55
+ %i[slows painfuls skips failures brokens errors].each do |issue_category|
56
+ # Only show categories for the most pressing issues after the suite runs, otherwise,
57
+ # suppress them until the more critical issues are resolved.
58
+ next unless show?(issue_category, results)
59
+
60
+ results.send(issue_category).each { |issue| issue_details(issue) }
61
+ end
62
+ rescue StandardError => e
63
+ message = "Sorry, but Minitest Heat couldn't display the details of any failures."
64
+ exception_guidance(message, e)
65
+ end
66
+
45
67
  def issue_details(issue)
46
68
  print_tokens Minitest::Heat::Output::Issue.new(issue).tokens
69
+ rescue StandardError => e
70
+ message = "Sorry, but Minitest Heat couldn't display output for a specific failure."
71
+ exception_guidance(message, e)
47
72
  end
48
73
 
49
74
  def marker(issue_type)
@@ -53,15 +78,56 @@ module Minitest
53
78
  def compact_summary(results, timer)
54
79
  newline
55
80
  print_tokens ::Minitest::Heat::Output::Results.new(results, timer).tokens
81
+ rescue StandardError => e
82
+ message = "Sorry, but Minitest Heat couldn't display the summary."
83
+ exception_guidance(message, e)
56
84
  end
57
85
 
58
86
  def heat_map(map)
59
87
  newline
60
88
  print_tokens ::Minitest::Heat::Output::Map.new(map).tokens
89
+ newline
90
+ rescue StandardError => e
91
+ message = "Sorry, but Minitest Heat couldn't display the heat map."
92
+ exception_guidance(message, e)
61
93
  end
62
94
 
63
95
  private
64
96
 
97
+ # Displays some guidance related to exceptions generated by Minitest Heat in order to help
98
+ # people get back on track (and ideally submit issues)
99
+ # @param message [String] a slightly more specific explanation of which part of minitest-heat
100
+ # caused the failure
101
+ # @param exception [Exception] the exception that caused the problem
102
+ #
103
+ # @return [void] displays the guidance to the console
104
+ def exception_guidance(message, exception)
105
+ newline
106
+ puts "#{message} Disabling Minitest Heat can get you back on track until the problem can be fixed."
107
+ puts 'Please use the following exception details to submit an issue at https://github.com/garrettdimon/minitest-heat/issues'
108
+ puts "#{exception.message}:"
109
+ exception.backtrace.each do |line|
110
+ puts " #{line}"
111
+ end
112
+ newline
113
+ end
114
+
115
+ def no_problems?(results)
116
+ !results.problems?
117
+ end
118
+
119
+ def no_problems_or_skips?(results)
120
+ !results.problems? && results.skips.none?
121
+ end
122
+
123
+ def show?(issue_category, results)
124
+ case issue_category
125
+ when :skips then no_problems?(results)
126
+ when :painfuls, :slows then no_problems_or_skips?(results)
127
+ else true
128
+ end
129
+ end
130
+
65
131
  def style_enabled?
66
132
  stream.tty?
67
133
  end
@@ -82,7 +148,11 @@ module Minitest
82
148
  def print_tokens(lines_of_tokens)
83
149
  lines_of_tokens.each do |tokens|
84
150
  tokens.each do |token|
85
- print Token.new(*token).to_s(token_format)
151
+ begin
152
+ print Token.new(*token).to_s(token_format)
153
+ rescue
154
+ puts token.inspect
155
+ end
86
156
  end
87
157
  newline
88
158
  end
@@ -11,9 +11,29 @@ 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
- @heat_map.add(*issue.to_hit) if issue.hit?
21
+
22
+ # If it's not a genuine problem, we're done here...
23
+ return unless issue.hit?
24
+
25
+ # ...otherwise update the heat map
26
+ update_heat_map(issue)
27
+ end
28
+
29
+ def update_heat_map(issue)
30
+ # For heat map purposes, only the project backtrace lines are interesting
31
+ pathname, line_number = issue.locations.project.to_a
32
+
33
+ # Backtrace is only relevant for exception-generating issues, not slows, skips, or failures
34
+ backtrace = issue.error? ? issue.locations.backtrace.project_locations : []
35
+
36
+ @heat_map.add(pathname, line_number, issue.type, backtrace: backtrace)
17
37
  end
18
38
 
19
39
  def problems?
@@ -37,11 +57,11 @@ module Minitest
37
57
  end
38
58
 
39
59
  def painfuls
40
- @painfuls ||= select_issues(:painful).sort_by(&:time).reverse
60
+ @painfuls ||= select_issues(:painful).sort_by(&:execution_time).reverse
41
61
  end
42
62
 
43
63
  def slows
44
- @slows ||= select_issues(:slow).sort_by(&:time).reverse
64
+ @slows ||= select_issues(:slow).sort_by(&:execution_time).reverse
45
65
  end
46
66
 
47
67
  private
@@ -35,7 +35,7 @@ module Minitest
35
35
  #
36
36
  # @return [Array<String>] the range of lines of code around
37
37
  def lines
38
- return [line] if max_line_count == 1
38
+ return [line].compact if max_line_count == 1
39
39
 
40
40
  file_lines[(line_numbers.first - 1)..(line_numbers.last - 1)]
41
41
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Minitest
4
4
  module Heat
5
- VERSION = '0.0.9'
5
+ VERSION = '0.0.13'
6
6
  end
7
7
  end
data/lib/minitest/heat.rb CHANGED
@@ -3,8 +3,8 @@
3
3
  require_relative 'heat/backtrace'
4
4
  require_relative 'heat/hit'
5
5
  require_relative 'heat/issue'
6
- require_relative 'heat/line'
7
6
  require_relative 'heat/location'
7
+ require_relative 'heat/locations'
8
8
  require_relative 'heat/map'
9
9
  require_relative 'heat/output'
10
10
  require_relative 'heat/results'
@@ -13,7 +13,8 @@ require_relative 'heat/timer'
13
13
  require_relative 'heat/version'
14
14
 
15
15
  module Minitest
16
- # Custom minitest reporter just for Reviewer. Focuses on printing directly actionable guidance.
16
+ # Custom Minitest reporter focused on generating output designed around efficiently identifying
17
+ # issues and potential solutions
17
18
  # - Colorize the Output
18
19
  # - What files had the most errors?
19
20
  # - Show the most impacted areas first.
@@ -2,25 +2,17 @@
2
2
 
3
3
  require_relative 'heat_reporter'
4
4
 
5
- module Minitest
6
- def self.plugin_heat_options(opts, _options)
7
- opts.on '--show-fast', 'Show failures as they happen instead of waiting for the entire suite.' do
8
- # Heat.show_fast!
9
- end
10
-
11
- # TODO: options.
12
- # 1. Fail Fast
13
- # 2. Don't worry about skips.
14
- # 3. Skip coverage.
15
- end
16
-
5
+ module Minitest # rubocop:disable Style/Documentation
17
6
  def self.plugin_heat_init(options)
18
- io = options[:io]
7
+ io = options.fetch(:io, $stdout)
19
8
 
20
- # Clean out the existing reporters.
21
- reporter.reporters = []
9
+ reporter.reporters.reject! do |reporter|
10
+ # Minitest Heat acts as a unified Progress *and* Summary reporter. Using other reporters of
11
+ # those types in conjunction with it creates some overly-verbose output
12
+ reporter.is_a?(ProgressReporter) || reporter.is_a?(SummaryReporter)
13
+ end
22
14
 
23
- # Use Reviewer as the sole reporter.
24
- reporter << HeatReporter.new(io, options)
15
+ # Hook up Reviewer
16
+ self.reporter.reporters << HeatReporter.new(io, options)
25
17
  end
26
18
  end
@@ -35,6 +35,8 @@ module Minitest
35
35
  :results
36
36
 
37
37
  def initialize(io = $stdout, options = {})
38
+ super()
39
+
38
40
  @options = options
39
41
 
40
42
  @timer = Heat::Timer.new
@@ -62,33 +64,39 @@ module Minitest
62
64
  def record(result)
63
65
  # Convert a Minitest Result into an "issue" to more consistently expose the data needed to
64
66
  # adjust the failure output to the type of failure
65
- issue = Heat::Issue.new(result)
67
+ issue = Heat::Issue.from_result(result)
68
+
69
+ # Note the number of assertions for the performance summary
70
+ timer.increment_counts(issue.assertions)
66
71
 
67
- timer.increment_counts(issue.result.assertions)
72
+ # Record the issue to show details later
68
73
  results.record(issue)
69
74
 
75
+ # Show the marker
70
76
  output.marker(issue.type)
77
+ rescue StandardError => e
78
+ display_exception_guidance(e)
71
79
  end
72
80
 
73
- # Outputs the summary of the run.
74
- def report
75
- timer.stop!
76
-
77
- # A couple of blank lines to create some breathing room
81
+ def display_exception_guidance(exception)
78
82
  output.newline
83
+ puts 'Sorry, but Minitest Heat encountered an exception recording an issue. Disabling Minitest Heat will get you back on track.'
84
+ puts 'Please use the following exception details to submit an issue at https://github.com/garrettdimon/minitest-heat/issues'
85
+ puts "#{exception.message}:"
86
+ exception.backtrace.each do |line|
87
+ puts " #{line}"
88
+ end
79
89
  output.newline
90
+ end
80
91
 
81
- # Issues start with the least critical and go up to the most critical so that the most
82
- # pressing issues are displayed at the bottom of the report in order to reduce scrolling.
83
- # This way, as you fix issues, the list gets shorter, and eventually the least critical
84
- # issues will be displayed without scrolling once more problematic issues are resolved.
85
- %i[slows painfuls skips failures brokens errors].each do |issue_category|
86
- next unless show?(issue_category)
92
+ # Outputs the summary of the run.
93
+ def report
94
+ timer.stop!
87
95
 
88
- results.send(issue_category).each { |issue| output.issue_details(issue) }
89
- end
96
+ # The list of individual issues and their associated details
97
+ output.issues_list(results)
90
98
 
91
- # Display a short summary of the total issue counts fore ach category as well as performance
99
+ # Display a short summary of the total issue counts for each category as well as performance
92
100
  # details for the test suite as a whole
93
101
  output.compact_summary(results, timer)
94
102
 
@@ -96,7 +104,7 @@ module Minitest
96
104
  # common sources of issues
97
105
  output.heat_map(results)
98
106
 
99
- # A blank line to create some breathing room
107
+ output.newline
100
108
  output.newline
101
109
  end
102
110
 
@@ -104,23 +112,5 @@ module Minitest
104
112
  def passed?
105
113
  results.errors.empty? && results.failures.empty?
106
114
  end
107
-
108
- private
109
-
110
- def no_problems?
111
- !results.problems?
112
- end
113
-
114
- def no_problems_or_skips?
115
- !results.problems? && !results.skips.any?
116
- end
117
-
118
- def show?(issue_category)
119
- case issue_category
120
- when :skips then no_problems?
121
- when :painfuls, :slows then no_problems_or_skips?
122
- else true
123
- end
124
- end
125
115
  end
126
116
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minitest-heat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Garrett Dimon
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-10-26 00:00:00.000000000 Z
11
+ date: 2021-11-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -141,12 +141,19 @@ files:
141
141
  - Rakefile
142
142
  - bin/console
143
143
  - bin/setup
144
+ - examples/exceptions.png
145
+ - examples/failures.png
146
+ - examples/map.png
147
+ - examples/markers.png
148
+ - examples/skips.png
149
+ - examples/slows.png
144
150
  - lib/minitest/heat.rb
145
151
  - lib/minitest/heat/backtrace.rb
152
+ - lib/minitest/heat/backtrace/line_parser.rb
146
153
  - lib/minitest/heat/hit.rb
147
154
  - lib/minitest/heat/issue.rb
148
- - lib/minitest/heat/line.rb
149
155
  - lib/minitest/heat/location.rb
156
+ - lib/minitest/heat/locations.rb
150
157
  - lib/minitest/heat/map.rb
151
158
  - lib/minitest/heat/output.rb
152
159
  - lib/minitest/heat/output/backtrace.rb
@@ -188,7 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
188
195
  - !ruby/object:Gem::Version
189
196
  version: '0'
190
197
  requirements: []
191
- rubygems_version: 3.1.6
198
+ rubygems_version: 3.2.22
192
199
  signing_key:
193
200
  specification_version: 4
194
201
  summary: Presents test results in a visual manner to guide you to where to look first.