minitest-heat 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,8 +2,8 @@
2
2
 
3
3
  require_relative 'output/backtrace'
4
4
  require_relative 'output/issue'
5
- require_relative 'output/location'
6
5
  require_relative 'output/map'
6
+ require_relative 'output/marker'
7
7
  require_relative 'output/results'
8
8
  require_relative 'output/source_code'
9
9
  require_relative 'output/token'
@@ -12,35 +12,6 @@ module Minitest
12
12
  module Heat
13
13
  # Friendly API for printing nicely-formatted output to the console
14
14
  class Output
15
- FORMATTERS = {
16
- error: [
17
- [ %i[error label], %i[muted spacer], %i[default test_name] ],
18
- [ %i[italicized summary], ],
19
- [ %i[default backtrace_summary] ],
20
- ],
21
- broken: [
22
- [ %i[broken label], %i[muted spacer], %i[default test_name], %i[muted spacer], %i[muted test_class] ],
23
- [ %i[italicized summary], ],
24
- [ %i[default backtrace_summary] ],
25
- ],
26
- failure: [
27
- [ %i[failure label], %i[muted spacer], %i[default test_name], %i[muted spacer], %i[muted test_class] ],
28
- [ %i[italicized summary] ],
29
- [ %i[muted short_location], ],
30
- [ %i[default source_summary], ],
31
- ],
32
- skipped: [
33
- [ %i[skipped label], %i[muted spacer], %i[default test_name], %i[muted spacer], %i[muted test_class] ],
34
- [ %i[italicized summary] ],
35
- [], # New Line
36
- ],
37
- slow: [
38
- [ %i[slow label], %i[muted spacer], %i[default test_name], %i[muted spacer], %i[default test_class] ],
39
- [ %i[bold slowness], %i[muted spacer], %i[default location], ],
40
- [], # New Line
41
- ]
42
- }
43
-
44
15
  attr_reader :stream
45
16
 
46
17
  def initialize(stream = $stdout)
@@ -59,106 +30,22 @@ module Minitest
59
30
  end
60
31
  alias newline puts
61
32
 
62
- # TOOD: Convert to output class
63
- # - This should likely live in the output/issue class
64
- # - Add a 'fail_fast' option that shows the issue as soon as the failure occurs
65
- def marker(value)
66
- case value
67
- when 'E' then text(:error, value)
68
- when 'B' then text(:failure, value)
69
- when 'F' then text(:failure, value)
70
- when 'S' then text(:skipped, value)
71
- else text(:success, value)
72
- end
73
- end
74
-
75
- # TOOD: Convert to output class
76
- # - This should likely live in the output/issue class
77
- # - There may be justification for creating different "strategies" for the various types
78
33
  def issue_details(issue)
79
- formatter = FORMATTERS[issue.type]
80
-
81
- formatter.each do |lines|
82
- lines.each do |tokens|
83
- style, content_method = *tokens
84
-
85
- if issue.respond_to?(content_method)
86
- # If it's an available method on issue, use that to get the content
87
- content = issue.send(content_method)
88
- text(style, content)
89
- else
90
- # Otherwise, fall back to output and pass issue to *it*
91
- send(content_method, issue)
92
- end
93
- end
94
- newline
95
- end
96
- end
97
-
98
- # TOOD: Convert to output class
99
- def heat_map(map)
100
- map.files.each do |file|
101
- pathname = Pathname(file[0])
102
-
103
- path = pathname.dirname.to_s
104
- filename = pathname.basename.to_s
105
-
106
- values = map.hits[pathname.to_s]
107
-
108
-
109
- text(:error, 'E' * values[:error].size) if values[:error]&.any?
110
- text(:broken, 'B' * values[:broken].size) if values[:broken]&.any?
111
- text(:failure, 'F' * values[:failure].size) if values[:failure]&.any?
112
-
113
- unless values[:error]&.any? || values[:broken]&.any? || values[:failure]&.any?
114
- text(:skipped, 'S' * values[:skipped].size) if values[:skipped]&.any?
115
- text(:painful, '—' * values[:painful].size) if values[:painful]&.any?
116
- text(:slow, '–' * values[:slow].size) if values[:slow]&.any?
117
- end
118
-
119
- text(:muted, ' ') if map.hits.any?
120
-
121
- text(:muted, "#{path.delete_prefix(Dir.pwd)}/")
122
- text(:default, filename)
123
-
124
- text(:muted, ':')
125
-
126
- all_line_numbers = values.fetch(:error, []) + values.fetch(:failure, [])
127
- all_line_numbers += values.fetch(:skipped, [])
128
-
129
- line_numbers = all_line_numbers.compact.uniq.sort
130
- line_numbers.each { |line_number| text(:muted, "#{line_number} ") }
131
- newline
132
- end
133
- newline
34
+ print_tokens Minitest::Heat::Output::Issue.new(issue).tokens
134
35
  end
135
36
 
136
- # TOOD: Convert to output class
137
- def test_name_summary(issue)
138
- text(:default, "#{issue.test_class} > #{issue.test_name}")
37
+ def marker(issue_type)
38
+ print_token Minitest::Heat::Output::Marker.new(issue_type).token
139
39
  end
140
40
 
141
41
  def compact_summary(results)
142
- results_tokens = ::Minitest::Heat::Output::Results.new(results).tokens
143
-
144
- newline
145
- print_tokens(results_tokens)
146
42
  newline
43
+ print_tokens ::Minitest::Heat::Output::Results.new(results).tokens
147
44
  end
148
45
 
149
- def backtrace_summary(issue)
150
- location = issue.location
151
-
152
- backtrace_tokens = ::Minitest::Heat::Output::Backtrace.new(location).tokens
153
- print_tokens(backtrace_tokens)
154
- end
155
-
156
- def source_summary(issue)
157
- filename = issue.location.project_file
158
- line_number = issue.location.project_failure_line
159
-
160
- source_code_tokens = ::Minitest::Heat::Output::SourceCode.new(filename, line_number).tokens
161
- print_tokens(source_code_tokens)
46
+ def heat_map(map)
47
+ newline
48
+ print_tokens ::Minitest::Heat::Output::Map.new(map).tokens
162
49
  end
163
50
 
164
51
  private
@@ -176,6 +63,10 @@ module Minitest
176
63
  style_enabled? ? :styled : :unstyled
177
64
  end
178
65
 
66
+ def print_token(token)
67
+ print Token.new(*token).to_s(token_format)
68
+ end
69
+
179
70
  def print_tokens(lines_of_tokens)
180
71
  lines_of_tokens.each do |tokens|
181
72
  tokens.each do |token|
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Minitest
4
4
  module Heat
5
+ # A collection of test failures
5
6
  class Results
6
-
7
7
  attr_reader :test_count,
8
8
  :assertion_count,
9
9
  :success_count,
@@ -20,6 +20,7 @@ module Minitest
20
20
  broken: [],
21
21
  failure: [],
22
22
  skipped: [],
23
+ painful: [],
23
24
  slow: []
24
25
  }
25
26
  @start_time = nil
@@ -69,12 +70,20 @@ module Minitest
69
70
  issues.fetch(:skipped) { [] }
70
71
  end
71
72
 
73
+ def painfuls
74
+ issues
75
+ .fetch(:painful) { [] }
76
+ .sort_by(&:time)
77
+ .reverse
78
+ .take(5)
79
+ end
80
+
72
81
  def slows
73
82
  issues
74
83
  .fetch(:slow) { [] }
75
- .sort { |issue| issue.time }
84
+ .sort_by(&:time)
76
85
  .reverse
77
- .take(3)
86
+ .take(5)
78
87
  end
79
88
 
80
89
  def errors?
@@ -93,6 +102,10 @@ module Minitest
93
102
  skips.any?
94
103
  end
95
104
 
105
+ def painfuls?
106
+ painfuls.any?
107
+ end
108
+
96
109
  def slows?
97
110
  slows.any?
98
111
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Minitest
2
4
  module Heat
3
- VERSION = "0.0.5"
5
+ VERSION = '0.0.6'
4
6
  end
5
7
  end
data/lib/minitest/heat.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'heat/backtrace'
4
+ require_relative 'heat/hit'
4
5
  require_relative 'heat/issue'
6
+ require_relative 'heat/line'
5
7
  require_relative 'heat/location'
6
8
  require_relative 'heat/map'
7
9
  require_relative 'heat/output'
@@ -3,12 +3,12 @@
3
3
  require_relative 'heat_reporter'
4
4
 
5
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
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
8
  # Heat.show_fast!
9
9
  end
10
10
 
11
- # TODO options.
11
+ # TODO: options.
12
12
  # 1. Fail Fast
13
13
  # 2. Don't worry about skips.
14
14
  # 3. Skip coverage.
@@ -18,9 +18,9 @@ module Minitest
18
18
  io = options[:io]
19
19
 
20
20
  # Clean out the existing reporters.
21
- self.reporter.reporters = []
21
+ reporter.reporters = []
22
22
 
23
23
  # Use Reviewer as the sole reporter.
24
- self.reporter << HeatReporter.new(io, options)
24
+ reporter << HeatReporter.new(io, options)
25
25
  end
26
26
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "heat"
3
+ require_relative 'heat'
4
4
 
5
5
  module Minitest
6
6
  # Custom minitest reporter to proactively identify likely culprits in test failures by focusing on
@@ -29,31 +29,31 @@ module Minitest
29
29
  # Pulls from minitest-color as well:
30
30
  # https://github.com/teoljungberg/minitest-color/blob/master/lib/minitest/color_plugin.rb
31
31
  class HeatReporter < AbstractReporter
32
-
33
32
  attr_reader :output,
34
33
  :options,
35
34
  :results,
36
35
  :map
37
36
 
38
37
  def initialize(io = $stdout, options = {})
39
- @output = Heat::Output.new(io)
40
38
  @options = options
41
39
 
42
- @results = Heat::Results.new
43
- @map = Heat::Map.new
40
+ @results = Heat::Results.new
41
+ @map = Heat::Map.new
42
+ @output = Heat::Output.new(io)
44
43
  end
45
44
 
46
45
  # Starts reporting on the run.
47
46
  def start
48
- output.puts
49
- output.puts
50
- @results.start_timer!
47
+ results.start_timer!
48
+
49
+ # A couple of blank lines to create some breathing room
50
+ output.newline
51
+ output.newline
51
52
  end
52
53
 
53
54
  # About to start running a test. This allows a reporter to show that it is starting or that we
54
55
  # are in the middle of a test run.
55
- def prerecord(klass, name)
56
- end
56
+ def prerecord(klass, name); end
57
57
 
58
58
  # Records the data from a result.
59
59
  # Minitest::Result source:
@@ -61,16 +61,17 @@ module Minitest
61
61
  def record(result)
62
62
  issue = Heat::Issue.new(result)
63
63
 
64
- @results.record(issue)
65
- @map.add(*issue.to_hit) if issue.hit?
64
+ results.record(issue)
65
+ map.add(*issue.to_hit) if issue.hit?
66
66
 
67
- output.marker(issue.marker)
67
+ output.marker(issue.type)
68
68
  end
69
69
 
70
70
  # Outputs the summary of the run.
71
71
  def report
72
- @results.stop_timer!
72
+ results.stop_timer!
73
73
 
74
+ # A couple of blank lines to create some breathing room
74
75
  output.newline
75
76
  output.newline
76
77
 
@@ -78,20 +79,20 @@ module Minitest
78
79
  # pressing issues are displayed at the bottom of the report in order to reduce scrolling.
79
80
  # This way, as you fix issues, the list gets shorter, and eventually the least critical
80
81
  # issues will be displayed without scrolling once more problematic issues are resolved.
81
- if results.failures.empty? && results.brokens.empty? && results.errors.empty? && results.skips.empty?
82
- results.slows.each { |issue| output.issue_details(issue) }
82
+ %i[slows painfuls skips failures brokens errors].each do |issue_category|
83
+ results.send(issue_category).each { |issue| output.issue_details(issue) }
83
84
  end
84
85
 
85
- if results.failures.empty? && results.brokens.empty? && results.errors.empty?
86
- results.skips.each { |issue| output.issue_details(issue) }
87
- end
88
-
89
- results.failures.each { |issue| output.issue_details(issue) }
90
- results.brokens.each { |issue| output.issue_details(issue) }
91
- results.errors.each { |issue| output.issue_details(issue) }
92
-
86
+ # Display a short summary of the total issue counts fore ach category as well as performance
87
+ # details for the test suite as a whole
93
88
  output.compact_summary(results)
89
+
90
+ # If there were issues, shows a short heat map summary of which files and lines were the most
91
+ # common sources of issues
94
92
  output.heat_map(map)
93
+
94
+ # A blank line to create some breathing room
95
+ output.newline
95
96
  end
96
97
 
97
98
  # Did this run pass?
@@ -21,8 +21,6 @@ Gem::Specification.new do |spec|
21
21
  spec.metadata['source_code_uri'] = 'https://github.com/garrettdimon/minitest-heat'
22
22
  spec.metadata['wiki_uri'] = 'https://github.com/garrettdimon/minitest-heat/wiki'
23
23
 
24
-
25
-
26
24
  # Specify which files should be added to the gem when it is released.
27
25
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
28
26
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
@@ -34,7 +32,11 @@ Gem::Specification.new do |spec|
34
32
 
35
33
  spec.add_runtime_dependency 'minitest'
36
34
 
35
+ spec.add_development_dependency 'awesome_print'
37
36
  spec.add_development_dependency 'dead_end'
38
37
  spec.add_development_dependency 'pry'
38
+ spec.add_development_dependency 'rubocop'
39
+ spec.add_development_dependency 'rubocop-minitest'
40
+ spec.add_development_dependency 'rubocop-rake'
39
41
  spec.add_development_dependency 'simplecov'
40
42
  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.5
4
+ version: 0.0.6
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-09-13 00:00:00.000000000 Z
11
+ date: 2021-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: awesome_print
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: dead_end
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +66,48 @@ dependencies:
52
66
  - - ">="
53
67
  - !ruby/object:Gem::Version
54
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop-minitest
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop-rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
55
111
  - !ruby/object:Gem::Dependency
56
112
  name: simplecov
57
113
  requirement: !ruby/object:Gem::Requirement
@@ -75,6 +131,7 @@ extensions: []
75
131
  extra_rdoc_files: []
76
132
  files:
77
133
  - ".gitignore"
134
+ - ".rubocop.yml"
78
135
  - ".travis.yml"
79
136
  - CODE_OF_CONDUCT.md
80
137
  - Gemfile
@@ -86,14 +143,16 @@ files:
86
143
  - bin/setup
87
144
  - lib/minitest/heat.rb
88
145
  - lib/minitest/heat/backtrace.rb
146
+ - lib/minitest/heat/hit.rb
89
147
  - lib/minitest/heat/issue.rb
148
+ - lib/minitest/heat/line.rb
90
149
  - lib/minitest/heat/location.rb
91
150
  - lib/minitest/heat/map.rb
92
151
  - lib/minitest/heat/output.rb
93
152
  - lib/minitest/heat/output/backtrace.rb
94
153
  - lib/minitest/heat/output/issue.rb
95
- - lib/minitest/heat/output/location.rb
96
154
  - lib/minitest/heat/output/map.rb
155
+ - lib/minitest/heat/output/marker.rb
97
156
  - lib/minitest/heat/output/results.rb
98
157
  - lib/minitest/heat/output/source_code.rb
99
158
  - lib/minitest/heat/output/token.rb
@@ -128,7 +187,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
128
187
  - !ruby/object:Gem::Version
129
188
  version: '0'
130
189
  requirements: []
131
- rubygems_version: 3.1.4
190
+ rubygems_version: 3.1.6
132
191
  signing_key:
133
192
  specification_version: 4
134
193
  summary: Presents test results in a visual manner to guide you to where to look first.
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Minitest
4
- module Heat
5
- class Output
6
- class Location
7
- attr_accessor :location
8
-
9
- def initialize(location)
10
- @location = location
11
- end
12
-
13
- def tokens
14
- end
15
-
16
- private
17
- end
18
- end
19
- end
20
- end