minitest-heat 0.0.6 → 0.0.10
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/.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 +28 -27
- data/lib/minitest/heat/issue.rb +92 -73
- data/lib/minitest/heat/location.rb +71 -53
- data/lib/minitest/heat/map.rb +14 -17
- data/lib/minitest/heat/output/backtrace.rb +28 -13
- data/lib/minitest/heat/output/issue.rb +93 -23
- data/lib/minitest/heat/output/map.rb +47 -24
- data/lib/minitest/heat/output/marker.rb +5 -3
- data/lib/minitest/heat/output/results.rb +32 -21
- 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 +70 -2
- data/lib/minitest/heat/results.rb +27 -81
- data/lib/minitest/heat/timer.rb +81 -0
- 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 +29 -23
- metadata +4 -3
- data/lib/minitest/heat/line.rb +0 -74
@@ -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,22 +3,29 @@
|
|
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
|
|
9
|
-
attr_accessor :results
|
10
|
+
attr_accessor :results, :timer
|
10
11
|
|
11
|
-
def_delegators :@results, :errors, :brokens, :failures, :skips, :painfuls, :slows, :problems
|
12
|
+
def_delegators :@results, :issues, :errors, :brokens, :failures, :skips, :painfuls, :slows, :problems?
|
12
13
|
|
13
|
-
def initialize(results)
|
14
|
+
def initialize(results, timer)
|
14
15
|
@results = results
|
16
|
+
@timer = timer
|
15
17
|
@tokens = []
|
16
18
|
end
|
17
19
|
|
18
20
|
def tokens
|
21
|
+
# Only show the issue type counts if there are issues
|
19
22
|
@tokens << [*issue_counts_tokens] if issue_counts_tokens&.any?
|
20
|
-
|
21
|
-
@tokens << [
|
23
|
+
|
24
|
+
@tokens << [
|
25
|
+
timing_token, spacer_token,
|
26
|
+
test_count_token, tests_performance_token, join_token,
|
27
|
+
assertions_count_token, assertions_performance_token
|
28
|
+
]
|
22
29
|
|
23
30
|
@tokens
|
24
31
|
end
|
@@ -33,7 +40,7 @@ module Minitest
|
|
33
40
|
end
|
34
41
|
|
35
42
|
def issue_counts_tokens
|
36
|
-
return unless
|
43
|
+
return unless issues.any?
|
37
44
|
|
38
45
|
counts = [
|
39
46
|
error_count_token,
|
@@ -45,10 +52,10 @@ module Minitest
|
|
45
52
|
].compact
|
46
53
|
|
47
54
|
# # Create an array of separator tokens one less than the total number of issue count tokens
|
48
|
-
|
55
|
+
spacer_tokens = Array.new(counts.size, spacer_token)
|
49
56
|
|
50
57
|
counts_with_separators = counts
|
51
|
-
.zip(
|
58
|
+
.zip(spacer_tokens) # Add separators between the counts
|
52
59
|
.flatten(1) # Flatten the zipped separators, but no more
|
53
60
|
|
54
61
|
counts_with_separators.pop # Remove the final trailing zipped separator that's not needed
|
@@ -74,33 +81,33 @@ module Minitest
|
|
74
81
|
end
|
75
82
|
|
76
83
|
def painful_count_token
|
77
|
-
style = problems? ? :muted : :painful
|
84
|
+
style = problems? || skips.any? ? :muted : :painful
|
78
85
|
issue_count_token(style, painfuls, name: 'Painfully Slow')
|
79
86
|
end
|
80
87
|
|
81
88
|
def slow_count_token
|
82
|
-
style = problems? ? :muted : :slow
|
89
|
+
style = problems? || skips.any? ? :muted : :slow
|
83
90
|
issue_count_token(style, slows, name: 'Slow')
|
84
91
|
end
|
85
92
|
|
86
|
-
def
|
87
|
-
[:
|
93
|
+
def test_count_token
|
94
|
+
[:default, pluralize(timer.test_count, 'test').to_s]
|
88
95
|
end
|
89
96
|
|
90
97
|
def tests_performance_token
|
91
|
-
[:default, "
|
98
|
+
[:default, " (#{timer.tests_per_second}/s)"]
|
92
99
|
end
|
93
100
|
|
94
|
-
def
|
95
|
-
[:default,
|
101
|
+
def assertions_count_token
|
102
|
+
[:default, pluralize(timer.assertion_count, 'assertion').to_s]
|
96
103
|
end
|
97
104
|
|
98
|
-
def
|
99
|
-
[:
|
105
|
+
def assertions_performance_token
|
106
|
+
[:default, " (#{timer.assertions_per_second}/s)"]
|
100
107
|
end
|
101
108
|
|
102
|
-
def
|
103
|
-
[:
|
109
|
+
def timing_token
|
110
|
+
[:bold, "#{timer.total_time.round(2)}s"]
|
104
111
|
end
|
105
112
|
|
106
113
|
def issue_count_token(type, collection, name: type.capitalize)
|
@@ -109,8 +116,12 @@ module Minitest
|
|
109
116
|
[type, pluralize(collection.size, name)]
|
110
117
|
end
|
111
118
|
|
112
|
-
def
|
113
|
-
[:
|
119
|
+
def spacer_token
|
120
|
+
Output::TOKENS[:spacer]
|
121
|
+
end
|
122
|
+
|
123
|
+
def join_token
|
124
|
+
[:default, ' with ']
|
114
125
|
end
|
115
126
|
end
|
116
127
|
end
|
@@ -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
@@ -12,6 +12,18 @@ module Minitest
|
|
12
12
|
module Heat
|
13
13
|
# Friendly API for printing nicely-formatted output to the console
|
14
14
|
class Output
|
15
|
+
SYMBOLS = {
|
16
|
+
middot: '·',
|
17
|
+
arrow: '➜',
|
18
|
+
lead: '|'
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
TOKENS = {
|
22
|
+
spacer: [:muted, " #{SYMBOLS[:middot]} "],
|
23
|
+
muted_arrow: [:muted, " #{SYMBOLS[:arrow]} "],
|
24
|
+
muted_lead: [:muted, "#{SYMBOLS[:lead]} "]
|
25
|
+
}.freeze
|
26
|
+
|
15
27
|
attr_reader :stream
|
16
28
|
|
17
29
|
def initialize(stream = $stdout)
|
@@ -30,26 +42,82 @@ module Minitest
|
|
30
42
|
end
|
31
43
|
alias newline puts
|
32
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
|
+
|
33
64
|
def issue_details(issue)
|
34
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)
|
35
69
|
end
|
36
70
|
|
37
71
|
def marker(issue_type)
|
38
72
|
print_token Minitest::Heat::Output::Marker.new(issue_type).token
|
39
73
|
end
|
40
74
|
|
41
|
-
def compact_summary(results)
|
75
|
+
def compact_summary(results, timer)
|
42
76
|
newline
|
43
|
-
print_tokens ::Minitest::Heat::Output::Results.new(results).tokens
|
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)
|
44
81
|
end
|
45
82
|
|
46
83
|
def heat_map(map)
|
47
84
|
newline
|
48
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
|
49
101
|
end
|
50
102
|
|
51
103
|
private
|
52
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
|
+
|
53
121
|
def style_enabled?
|
54
122
|
stream.tty?
|
55
123
|
end
|
@@ -4,119 +4,65 @@ module Minitest
|
|
4
4
|
module Heat
|
5
5
|
# A collection of test failures
|
6
6
|
class Results
|
7
|
-
attr_reader :
|
8
|
-
:assertion_count,
|
9
|
-
:success_count,
|
10
|
-
:issues,
|
11
|
-
:start_time,
|
12
|
-
:stop_time
|
7
|
+
attr_reader :issues, :heat_map
|
13
8
|
|
14
9
|
def initialize
|
15
|
-
@
|
16
|
-
@
|
17
|
-
@success_count = 0
|
18
|
-
@issues = {
|
19
|
-
error: [],
|
20
|
-
broken: [],
|
21
|
-
failure: [],
|
22
|
-
skipped: [],
|
23
|
-
painful: [],
|
24
|
-
slow: []
|
25
|
-
}
|
26
|
-
@start_time = nil
|
27
|
-
@stop_time = nil
|
10
|
+
@issues = []
|
11
|
+
@heat_map = Heat::Map.new
|
28
12
|
end
|
29
13
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
def total_time
|
39
|
-
delta = @stop_time - @start_time
|
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]
|
18
|
+
def record(issue)
|
19
|
+
# Record everything—even if it's a success
|
20
|
+
@issues.push(issue)
|
40
21
|
|
41
|
-
#
|
42
|
-
|
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?
|
43
24
|
end
|
44
25
|
|
45
|
-
def
|
46
|
-
|
47
|
-
|
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
|
48
30
|
|
49
|
-
|
50
|
-
(assertion_count / total_time).round(2)
|
31
|
+
@heat_map.add(pathname, line_number, issue.type)
|
51
32
|
end
|
52
33
|
|
53
34
|
def problems?
|
54
|
-
errors? || brokens? || failures?
|
35
|
+
errors.any? || brokens.any? || failures.any?
|
55
36
|
end
|
56
37
|
|
57
38
|
def errors
|
58
|
-
|
39
|
+
@errors ||= select_issues(:error)
|
59
40
|
end
|
60
41
|
|
61
42
|
def brokens
|
62
|
-
|
43
|
+
@brokens ||= select_issues(:broken)
|
63
44
|
end
|
64
45
|
|
65
46
|
def failures
|
66
|
-
|
47
|
+
@failures ||= select_issues(:failure)
|
67
48
|
end
|
68
49
|
|
69
50
|
def skips
|
70
|
-
|
51
|
+
@skips ||= select_issues(:skipped)
|
71
52
|
end
|
72
53
|
|
73
54
|
def painfuls
|
74
|
-
|
75
|
-
.fetch(:painful) { [] }
|
76
|
-
.sort_by(&:time)
|
77
|
-
.reverse
|
78
|
-
.take(5)
|
55
|
+
@painfuls ||= select_issues(:painful).sort_by(&:execution_time).reverse
|
79
56
|
end
|
80
57
|
|
81
58
|
def slows
|
82
|
-
|
83
|
-
.fetch(:slow) { [] }
|
84
|
-
.sort_by(&:time)
|
85
|
-
.reverse
|
86
|
-
.take(5)
|
87
|
-
end
|
88
|
-
|
89
|
-
def errors?
|
90
|
-
errors.any?
|
91
|
-
end
|
92
|
-
|
93
|
-
def brokens?
|
94
|
-
brokens.any?
|
59
|
+
@slows ||= select_issues(:slow).sort_by(&:execution_time).reverse
|
95
60
|
end
|
96
61
|
|
97
|
-
|
98
|
-
failures.any?
|
99
|
-
end
|
100
|
-
|
101
|
-
def skips?
|
102
|
-
skips.any?
|
103
|
-
end
|
104
|
-
|
105
|
-
def painfuls?
|
106
|
-
painfuls.any?
|
107
|
-
end
|
108
|
-
|
109
|
-
def slows?
|
110
|
-
slows.any?
|
111
|
-
end
|
112
|
-
|
113
|
-
def record(issue)
|
114
|
-
@test_count += 1
|
115
|
-
@assertion_count += issue.result.assertions
|
116
|
-
@success_count += 1 if issue.result.passed?
|
62
|
+
private
|
117
63
|
|
118
|
-
|
119
|
-
|
64
|
+
def select_issues(issue_type)
|
65
|
+
issues.select { |issue| issue.type == issue_type }
|
120
66
|
end
|
121
67
|
end
|
122
68
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Minitest
|
4
|
+
module Heat
|
5
|
+
# Provides a timer to keep track of the full test suite duration and provide convenient methods
|
6
|
+
# for calculating tests/second and assertions/second
|
7
|
+
class Timer
|
8
|
+
attr_reader :test_count, :assertion_count, :start_time, :stop_time
|
9
|
+
|
10
|
+
# Creates an instance of a timer to be used for the duration of a test suite run
|
11
|
+
#
|
12
|
+
# @return [self]
|
13
|
+
def initialize
|
14
|
+
@test_count = 0
|
15
|
+
@assertion_count = 0
|
16
|
+
|
17
|
+
@start_time = nil
|
18
|
+
@stop_time = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
# Records the start time for the full test suite using `Minitest.clock_time`
|
22
|
+
#
|
23
|
+
# @return [Float] the Minitest.clock_time
|
24
|
+
def start!
|
25
|
+
@start_time = Minitest.clock_time
|
26
|
+
end
|
27
|
+
|
28
|
+
# Records the stop time for the full test suite using `Minitest.clock_time`
|
29
|
+
#
|
30
|
+
# @return [Float] the Minitest.clock_time
|
31
|
+
def stop!
|
32
|
+
@stop_time = Minitest.clock_time
|
33
|
+
end
|
34
|
+
|
35
|
+
# Calculates the total time take for the full test suite to run while ensuring it never
|
36
|
+
# returns a zero that would be problematic as a denomitor in calculating average times
|
37
|
+
#
|
38
|
+
# @return [Float] the clocktime duration of the test suite run in seconds
|
39
|
+
def total_time
|
40
|
+
# Don't return 0. The time can end up being 0 for a new or realy fast test suite, and
|
41
|
+
# dividing by 0 doesn't go well when determining average time, so this ensures it uses a
|
42
|
+
# close-enough-but-not-zero value.
|
43
|
+
delta.zero? ? 0.01 : delta
|
44
|
+
end
|
45
|
+
|
46
|
+
# Records the test and assertion counts for a given test outcome
|
47
|
+
# @param count [Integer] the number of assertions from the test
|
48
|
+
#
|
49
|
+
# @return [void]
|
50
|
+
def increment_counts(count)
|
51
|
+
@test_count += 1
|
52
|
+
@assertion_count += count
|
53
|
+
end
|
54
|
+
|
55
|
+
# Provides a nice rounded answer for about how many tests were completed per second
|
56
|
+
#
|
57
|
+
# @return [Float] the average number of tests completed per second
|
58
|
+
def tests_per_second
|
59
|
+
(test_count / total_time).round(2)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Provides a nice rounded answer for about how many assertions were completed per second
|
63
|
+
#
|
64
|
+
# @return [Float] the average number of assertions completed per second
|
65
|
+
def assertions_per_second
|
66
|
+
(assertion_count / total_time).round(2)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# The total time the test suite was running.
|
72
|
+
#
|
73
|
+
# @return [Float] the time in seconds elapsed between starting the timer and stopping it
|
74
|
+
def delta
|
75
|
+
return 0 unless start_time && stop_time
|
76
|
+
|
77
|
+
stop_time - start_time
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/lib/minitest/heat.rb
CHANGED
@@ -3,16 +3,17 @@
|
|
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'
|
10
9
|
require_relative 'heat/results'
|
11
10
|
require_relative 'heat/source'
|
11
|
+
require_relative 'heat/timer'
|
12
12
|
require_relative 'heat/version'
|
13
13
|
|
14
14
|
module Minitest
|
15
|
-
# Custom
|
15
|
+
# Custom Minitest reporter focused on generating output designed around efficiently identifying
|
16
|
+
# issues and potential solutions
|
16
17
|
# - Colorize the Output
|
17
18
|
# - What files had the most errors?
|
18
19
|
# - Show the most impacted areas first.
|
data/lib/minitest/heat_plugin.rb
CHANGED
@@ -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
|
7
|
+
io = options.fetch(:io, $stdout)
|
19
8
|
|
20
|
-
|
21
|
-
|
9
|
+
self.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
|
-
#
|
24
|
-
reporter << HeatReporter.new(io, options)
|
15
|
+
# Hook up Reviewer
|
16
|
+
self.reporter.reporters << HeatReporter.new(io, options)
|
25
17
|
end
|
26
18
|
end
|
@@ -31,20 +31,22 @@ module Minitest
|
|
31
31
|
class HeatReporter < AbstractReporter
|
32
32
|
attr_reader :output,
|
33
33
|
:options,
|
34
|
-
:
|
35
|
-
:
|
34
|
+
:timer,
|
35
|
+
:results
|
36
36
|
|
37
37
|
def initialize(io = $stdout, options = {})
|
38
|
+
super()
|
39
|
+
|
38
40
|
@options = options
|
39
41
|
|
42
|
+
@timer = Heat::Timer.new
|
40
43
|
@results = Heat::Results.new
|
41
|
-
@map = Heat::Map.new
|
42
44
|
@output = Heat::Output.new(io)
|
43
45
|
end
|
44
46
|
|
45
47
|
# Starts reporting on the run.
|
46
48
|
def start
|
47
|
-
|
49
|
+
timer.start!
|
48
50
|
|
49
51
|
# A couple of blank lines to create some breathing room
|
50
52
|
output.newline
|
@@ -56,43 +58,47 @@ module Minitest
|
|
56
58
|
def prerecord(klass, name); end
|
57
59
|
|
58
60
|
# Records the data from a result.
|
61
|
+
#
|
59
62
|
# Minitest::Result source:
|
60
63
|
# https://github.com/seattlerb/minitest/blob/f4f57afaeb3a11bd0b86ab0757704cb78db96cf4/lib/minitest.rb#L504
|
61
64
|
def record(result)
|
62
|
-
issue
|
65
|
+
# Convert a Minitest Result into an "issue" to more consistently expose the data needed to
|
66
|
+
# adjust the failure output to the type of failure
|
67
|
+
issue = Heat::Issue.from_result(result)
|
68
|
+
|
69
|
+
# Note the number of assertions for the performance summary
|
70
|
+
timer.increment_counts(issue.assertions)
|
63
71
|
|
72
|
+
# Record the issue to show details later
|
64
73
|
results.record(issue)
|
65
|
-
map.add(*issue.to_hit) if issue.hit?
|
66
74
|
|
75
|
+
# Show the marker
|
67
76
|
output.marker(issue.type)
|
77
|
+
rescue => e
|
78
|
+
output.newline
|
79
|
+
puts "Sorry, but Minitest Heat encountered an exception recording an issue. Disabling Minitest Heat will get you back on track."
|
80
|
+
puts "Please use the following exception details to submit an issue at https://github.com/garrettdimon/minitest-heat/issues"
|
81
|
+
puts "#{e.message}:"
|
82
|
+
e.backtrace.each do |line|
|
83
|
+
puts " #{line}"
|
84
|
+
end
|
85
|
+
output.newline
|
68
86
|
end
|
69
87
|
|
70
88
|
# Outputs the summary of the run.
|
71
89
|
def report
|
72
|
-
|
73
|
-
|
74
|
-
# A couple of blank lines to create some breathing room
|
75
|
-
output.newline
|
76
|
-
output.newline
|
90
|
+
timer.stop!
|
77
91
|
|
78
|
-
#
|
79
|
-
|
80
|
-
# This way, as you fix issues, the list gets shorter, and eventually the least critical
|
81
|
-
# issues will be displayed without scrolling once more problematic issues are resolved.
|
82
|
-
%i[slows painfuls skips failures brokens errors].each do |issue_category|
|
83
|
-
results.send(issue_category).each { |issue| output.issue_details(issue) }
|
84
|
-
end
|
92
|
+
# The list of individual issues and their associated details
|
93
|
+
output.issues_list(results)
|
85
94
|
|
86
95
|
# Display a short summary of the total issue counts fore ach category as well as performance
|
87
96
|
# details for the test suite as a whole
|
88
|
-
output.compact_summary(results)
|
97
|
+
output.compact_summary(results, timer)
|
89
98
|
|
90
99
|
# If there were issues, shows a short heat map summary of which files and lines were the most
|
91
100
|
# common sources of issues
|
92
|
-
output.heat_map(
|
93
|
-
|
94
|
-
# A blank line to create some breathing room
|
95
|
-
output.newline
|
101
|
+
output.heat_map(results)
|
96
102
|
end
|
97
103
|
|
98
104
|
# Did this run pass?
|