test-prof 1.6.3 → 1.6.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 39f68f82427dfea9fb815614f6fce02fdfd7291fa1227e5c2a224f4c6483e091
4
- data.tar.gz: cd76ddbed3548fa987b8828727e7ddd6f81639b39b8d06d5e8c41f7aa84fba10
3
+ metadata.gz: f0609d37fef8dc561de55a0f192414738049f53a4c47772fad6d6f1adaa596a1
4
+ data.tar.gz: b7bef544e98f887c86650268907adca116b30f87bf6d9c7c2a846ac6b2fe3649
5
5
  SHA512:
6
- metadata.gz: 329db71243834a0641c490b3daaf77cfc5c352b1995c9b01dac49c0824619070d372e5eda7d64f76d97226087cfb2b9ad6d7e58823297e0e8ac53e5d74d82878
7
- data.tar.gz: 72830459616db6692c3c58621457a459122aadc8609472f1a38b827025a19da11784b68aee86abc27d535ca1ecf474b18cd45d0c60c33b5490de3f4c0a314d18
6
+ metadata.gz: fcb4fc774acc4ac145817c00a7b8530151791481d32016375d54c5519c430dca9b2cdfe7064d1f05f633697b55ddbeefc6343b38c82e81944304481f24845ab7
7
+ data.tar.gz: e011f71777982864fd1dc5c1537872631eb3aeada674e6aa8e5be1e70648feee519eac9b02e1175f2b42117d07cd2fb1ff0e26c1fa0a2e4adc2b721cda780df9
data/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  ## master (unreleased)
4
4
 
5
+ ## 1.6.4 (2026-10-09)
6
+
7
+ - Fix `RSpecDissect` writing bold escape sequences when the output is not a TTY. ([@meganemura][])
8
+
9
+ - Fix `before_all` with parallel Minitest 6. ([@GabrielNagy][])
10
+
11
+ - Add `EVENT_PROF_FORMAT` (`json`) support to EventProf along with the printers infrastructure (`Printers::Simple`, `Printers::Json`). ([@lHydra][])
12
+
13
+ - Fix `before_all` Minitest `parallelize` patch rejecting Rails 7.0+ keyword arguments (`threshold:`, `parallelize_databases:`). ([@hnegishi][])
14
+
5
15
  ## 1.6.3 (2026-07-20)
6
16
 
7
17
  - Fix `before_all` firing N times in Minitest 6. ([@palkan][])
@@ -519,3 +529,5 @@ See [changelog](https://github.com/test-prof/test-prof/blob/v0.8.0/CHANGELOG.md)
519
529
  [@Rylan12]: https://github.com/Rylan12
520
530
  [@kddnewton]: https://github.com/kddnewton
521
531
  [@moznion]: https://github.com/moznion
532
+ [@hnegishi]: https://github.com/hnegishi
533
+ [@meganemura]: https://github.com/meganemura
data/README.md CHANGED
@@ -37,12 +37,9 @@ TestProf toolbox aims to help you identify bottlenecks in your test suite. It co
37
37
  </a>
38
38
  </p>
39
39
 
40
- <p align="center">
41
- <a href="https://evilmartians.com/?utm_source=test-prof">
42
- <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg"
43
- alt="Sponsored by Evil Martians" width="236" height="54">
44
- </a>
45
- </p>
40
+ <br/>
41
+
42
+ <img src="https://cdn.evilmartians.com/badges/logo-no-label.svg" alt="Evil Martians logo" width="22" height="16" /> <b>TestProf</b> is built by <b><a href="https://evilmartians.com/">Evil Martians</a></b>, an American design and engineering consultancy for <b>developer tools, AI, and cybersecurity startups</b>.
46
43
 
47
44
  ## Who uses TestProf
48
45
 
@@ -35,6 +35,9 @@ module Minitest # :nodoc:
35
35
  opts.on "--event-prof-per-example", TrueClass, "Includes examples metrics to results" do |flag|
36
36
  options[:per_example] = flag
37
37
  end
38
+ opts.on "--event-prof-format=FORMAT", "Defines output FORMAT for results" do |format|
39
+ options[:event_prof_format] = format
40
+ end
38
41
  opts.on "--factory-doctor", TrueClass, "Enable Factory Doctor for your examples" do |flag|
39
42
  options[:fdoc] = flag
40
43
  end
@@ -10,47 +10,87 @@ module TestProf
10
10
  class << self
11
11
  if ::ActiveRecord::Base.connection.pool.respond_to?(:pin_connection!)
12
12
  def begin_transaction
13
- subscribe!
13
+ # don't rely on connection_handler.connection_pool_list,
14
+ # track pinned pools ourselves
15
+ # see https://github.com/rails/rails/pull/58489
16
+ pinned_pools = []
17
+ pinned_pools_stack.push(pinned_pools)
18
+ subscribe! if pinned_pools_stack.size == 1
19
+
14
20
  ::ActiveRecord::Base.connection_handler.connection_pool_list(:writing).each do |pool|
15
- pool.pin_connection!(true)
21
+ pin_pool!(pool)
22
+ pinned_pools << pool
16
23
  end
17
24
  end
18
25
 
19
26
  def rollback_transaction
20
- ::ActiveRecord::Base.connection_handler.connection_pool_list(:writing).each do |pool|
21
- pool.unpin_connection!
22
- end
23
- unsubscribe!
27
+ pinned_pools = pinned_pools_stack.pop
28
+ pinned_pools&.each { |pool| unpin_pool!(pool) }
29
+ ensure
30
+ unsubscribe! if pinned_pools_stack.empty?
24
31
  end
25
32
 
26
- def subscribe!
27
- Thread.current[:before_all_subscription_count] ||= 0
28
- Thread.current[:before_all_subscription_count] += 1
33
+ def pinned_pools_stack
34
+ Thread.current[:before_all_pinned_pools_stack] ||= []
35
+ end
29
36
 
30
- return unless Thread.current[:before_all_subscription_count] == 1
37
+ def subscribe!
38
+ # notifications are not Thread-pinned, so
39
+ # we must keep a reference to the current stack
40
+ stack = pinned_pools_stack
31
41
 
32
42
  Thread.current[:before_all_connection_subscriber] = ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload|
33
43
  connection_name = payload[:connection_name] if payload.key?(:connection_name)
34
44
  shard = payload[:shard] if payload.key?(:shard)
45
+ # Use the role of the established connection, not the current one
46
+ role = payload.fetch(:role, ::ActiveRecord::Base.current_role)
35
47
  next unless connection_name
36
48
 
37
- pool = ::ActiveRecord::Base.connection_handler.retrieve_connection_pool(connection_name, shard: shard)
49
+ pool = ::ActiveRecord::Base.connection_handler.retrieve_connection_pool(connection_name, role: role, shard: shard)
38
50
  next unless pool && pool.role == :writing
39
51
 
40
- pool.pin_connection!(true)
52
+ pinned_pools = stack.last
53
+ next if pinned_pools.nil? || pinned_pools.include?(pool)
54
+
55
+ pin_pool!(pool)
56
+ pinned_pools << pool
41
57
  end
42
58
  end
43
59
 
44
60
  def unsubscribe!
45
- return unless Thread.current[:before_all_subscription_count]
46
-
47
- Thread.current[:before_all_subscription_count] -= 1
48
-
49
- return unless Thread.current[:before_all_subscription_count] == 0 && Thread.current[:before_all_connection_subscriber]
61
+ return unless Thread.current[:before_all_connection_subscriber]
50
62
 
51
63
  ActiveSupport::Notifications.unsubscribe(Thread.current[:before_all_connection_subscriber])
52
64
  Thread.current[:before_all_connection_subscriber] = nil
53
65
  end
66
+
67
+ # Rails 8.2+
68
+ if ::ActiveRecord.version >= Gem::Version.new("8.2.0.alpha")
69
+ def pin_pool!(pool)
70
+ pool.pin_connection!(true)
71
+ pool.lease_connection.begin_transaction(joinable: false, _lazy: false)
72
+ end
73
+
74
+ def unpin_pool!(pool)
75
+ connection = pool.lease_connection
76
+ if connection.transaction_open?
77
+ connection.rollback_transaction
78
+ else
79
+ warn "!!! before_all transaction has been already rollbacked and could work incorrectly"
80
+ connection.reset!
81
+ end
82
+ ensure
83
+ pool.unpin_connection!
84
+ end
85
+ else
86
+ def pin_pool!(pool)
87
+ pool.pin_connection!(true)
88
+ end
89
+
90
+ def unpin_pool!(pool)
91
+ pool.unpin_connection!
92
+ end
93
+ end
54
94
  else
55
95
  def all_connections
56
96
  @all_connections ||= if ::ActiveRecord::Base.respond_to? :connects_to
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "minitest/base_reporter"
4
- require "minitest/event_prof_formatter"
5
4
 
6
5
  module Minitest
7
6
  module TestProf
@@ -12,7 +11,6 @@ module Minitest
12
11
 
13
12
  log :info, "EventProf enabled (#{@profiler.events.join(", ")})"
14
13
 
15
- @formatter = EventProfFormatter.new(@profiler)
16
14
  @current_group = nil
17
15
  @current_example = nil
18
16
  end
@@ -32,8 +30,7 @@ module Minitest
32
30
 
33
31
  def report
34
32
  @profiler.group_finished(@current_group)
35
- result = @formatter.prepare_results
36
- log :info, result
33
+ ::TestProf::EventProf.config.printer.dump(@profiler)
37
34
  end
38
35
 
39
36
  private
@@ -64,6 +61,7 @@ module Minitest
64
61
  def configure_profiler(options)
65
62
  ::TestProf::EventProf.configure do |config|
66
63
  config.event = options[:event]
64
+ config.format = options[:event_prof_format] if options[:event_prof_format]
67
65
  config.rank_by = options[:rank_by] if options[:rank_by]
68
66
  config.top_count = options[:top_count] if options[:top_count]
69
67
  config.per_example = options[:per_example] if options[:per_example]
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TestProf
4
+ module EventProf
5
+ module Printers
6
+ module Base
7
+ include TestProf::Logging
8
+
9
+ private
10
+
11
+ def describe_group(id)
12
+ id.is_a?(Hash) ? id[:name] : id.top_level_description
13
+ end
14
+
15
+ def describe_example(id)
16
+ id.is_a?(Hash) ? id[:name] : id.description
17
+ end
18
+
19
+ def locate(id)
20
+ id.is_a?(Hash) ? id[:location] : id.metadata[:location]
21
+ end
22
+
23
+ def time_percentage(time, total_time)
24
+ (time / total_time * 100).round(2)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_prof/event_prof/printers/base"
4
+ require "test_prof/ext/float_duration"
5
+
6
+ module TestProf
7
+ module EventProf
8
+ module Printers
9
+ module Json # :nodoc: all
10
+ class << self
11
+ using TestProf::FloatDuration
12
+
13
+ include Base
14
+
15
+ def dump(profilers)
16
+ data = []
17
+ profilers.each { |profiler| data << convert_results(profiler) }
18
+
19
+ outpath = TestProf.artifact_path("event-prof.json")
20
+ File.write(outpath, data.to_json)
21
+
22
+ log :info, "EventProf results to JSON: #{outpath}"
23
+ end
24
+
25
+ private
26
+
27
+ def convert_results(profiler)
28
+ result = profiler.results
29
+
30
+ {
31
+ event: profiler.event,
32
+ total_time: profiler.total_time.duration,
33
+ total_count: profiler.total_count,
34
+ absolute_run_time: profiler.absolute_run_time.duration,
35
+ time_percentage: time_percentage(profiler.total_time, profiler.absolute_run_time),
36
+ rank_by: profiler.rank_by,
37
+ top_count: profiler.top_count,
38
+ groups: result[:groups].map { |group| convert_group(group) }
39
+ }.tap do |data|
40
+ next unless result[:examples]
41
+
42
+ data[:examples] = result[:examples].map { |example| convert_example(example) }
43
+ end
44
+ end
45
+
46
+ def convert_group(group)
47
+ {
48
+ description: describe_group(group[:id]),
49
+ location: locate(group[:id]),
50
+ time: group[:time].duration,
51
+ run_time: group[:run_time].duration,
52
+ time_percentage: time_percentage(group[:time], group[:run_time]),
53
+ count: group[:count],
54
+ examples: group[:examples]
55
+ }
56
+ end
57
+
58
+ def convert_example(example)
59
+ {
60
+ description: describe_example(example[:id]),
61
+ location: locate(example[:id]),
62
+ time: example[:time].duration,
63
+ run_time: example[:run_time].duration,
64
+ time_percentage: time_percentage(example[:time], example[:run_time]),
65
+ count: example[:count]
66
+ }
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_prof/event_prof/printers/base"
4
+ require "test_prof/ext/float_duration"
5
+ require "test_prof/ext/string_truncate"
6
+
7
+ module TestProf
8
+ module EventProf
9
+ module Printers
10
+ module Simple # :nodoc: all
11
+ class << self
12
+ using TestProf::FloatDuration
13
+ using TestProf::StringTruncate
14
+
15
+ include Base
16
+
17
+ def dump(profilers)
18
+ profilers.each { |profiler| dump_profiler(profiler) }
19
+ end
20
+
21
+ private
22
+
23
+ def dump_profiler(profiler)
24
+ result = profiler.results
25
+ time_percentage = time_percentage(profiler.total_time, profiler.absolute_run_time)
26
+
27
+ msgs = []
28
+
29
+ msgs <<
30
+ <<~MSG
31
+ EventProf results for #{profiler.event}
32
+
33
+ Total time: #{profiler.total_time.duration} of #{profiler.absolute_run_time.duration} (#{time_percentage}%)
34
+ Total events: #{profiler.total_count}
35
+
36
+ Top #{profiler.top_count} slowest suites (by #{profiler.rank_by}):
37
+
38
+ MSG
39
+
40
+ result[:groups].each do |group|
41
+ description = describe_group(group[:id])
42
+ location = locate(group[:id])
43
+ time = group[:time]
44
+ run_time = group[:run_time]
45
+ time_percentage = time_percentage(time, run_time)
46
+
47
+ msgs <<
48
+ <<~GROUP
49
+ #{description.truncate} (#{location}) – #{time.duration} (#{group[:count]} / #{group[:examples]}) of #{run_time.duration} (#{time_percentage}%)
50
+ GROUP
51
+ end
52
+
53
+ if result[:examples]
54
+ msgs << "\nTop #{profiler.top_count} slowest tests (by #{profiler.rank_by}):\n\n"
55
+
56
+ result[:examples].each do |example|
57
+ description = describe_example(example[:id])
58
+ location = locate(example[:id])
59
+ time = example[:time]
60
+ run_time = example[:run_time]
61
+ time_percentage = time_percentage(time, run_time)
62
+
63
+ msgs <<
64
+ <<~GROUP
65
+ #{description.truncate} (#{location}) – #{time.duration} (#{example[:count]}) of #{run_time.duration} (#{time_percentage}%)
66
+ GROUP
67
+ end
68
+ end
69
+
70
+ log :info, msgs.join
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -1,16 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "test_prof/ext/float_duration"
4
- require "test_prof/ext/string_truncate"
5
-
6
3
  module TestProf
7
4
  module EventProf
8
5
  class RSpecListener # :nodoc:
9
6
  include Logging
10
7
 
11
- using FloatDuration
12
- using StringTruncate
13
-
14
8
  NOTIFICATIONS = %i[
15
9
  example_group_started
16
10
  example_group_finished
@@ -43,59 +37,11 @@ module TestProf
43
37
  end
44
38
 
45
39
  def print
46
- @profiler.each(&method(:report))
47
- end
48
-
49
- def report(profiler)
50
- result = profiler.results
51
- time_percentage = time_percentage(profiler.total_time, profiler.absolute_run_time)
52
-
53
- msgs = []
54
-
55
- msgs <<
56
- <<~MSG
57
- EventProf results for #{profiler.event}
58
-
59
- Total time: #{profiler.total_time.duration} of #{profiler.absolute_run_time.duration} (#{time_percentage}%)
60
- Total events: #{profiler.total_count}
40
+ EventProf.config.printer.dump(@profiler)
61
41
 
62
- Top #{profiler.top_count} slowest suites (by #{profiler.rank_by}):
42
+ return unless EventProf.config.stamp?
63
43
 
64
- MSG
65
-
66
- result[:groups].each do |group|
67
- description = group[:id].top_level_description
68
- location = group[:id].metadata[:location]
69
- time = group[:time]
70
- run_time = group[:run_time]
71
- time_percentage = time_percentage(time, run_time)
72
-
73
- msgs <<
74
- <<~GROUP
75
- #{description.truncate} (#{location}) – #{time.duration} (#{group[:count]} / #{group[:examples]}) of #{run_time.duration} (#{time_percentage}%)
76
- GROUP
77
- end
78
-
79
- if result[:examples]
80
- msgs << "\nTop #{profiler.top_count} slowest tests (by #{profiler.rank_by}):\n\n"
81
-
82
- result[:examples].each do |example|
83
- description = example[:id].description
84
- location = example[:id].metadata[:location]
85
- time = example[:time]
86
- run_time = example[:run_time]
87
- time_percentage = time_percentage(time, run_time)
88
-
89
- msgs <<
90
- <<~GROUP
91
- #{description.truncate} (#{location}) – #{time.duration} (#{example[:count]}) of #{run_time.duration} (#{time_percentage}%)
92
- GROUP
93
- end
94
- end
95
-
96
- log :info, msgs.join
97
-
98
- stamp!(profiler) if EventProf.config.stamp?
44
+ @profiler.each { |profiler| stamp!(profiler) }
99
45
  end
100
46
 
101
47
  def stamp!(profiler)
@@ -130,10 +76,6 @@ module TestProf
130
76
 
131
77
  log :info, msgs.join
132
78
  end
133
-
134
- def time_percentage(time, total_time)
135
- (time / total_time * 100).round(2)
136
- end
137
79
  end
138
80
  end
139
81
  end
@@ -2,6 +2,9 @@
2
2
 
3
3
  require "test_prof/rspec_stamp"
4
4
  require "test_prof/event_prof/profiler"
5
+ require "test_prof/event_prof/printers/base"
6
+ require "test_prof/event_prof/printers/simple"
7
+ require "test_prof/event_prof/printers/json"
5
8
  require "test_prof/event_prof/instrumentations/active_support"
6
9
  require "test_prof/event_prof/monitor"
7
10
  require "test_prof/utils/sized_ordered_set"
@@ -34,7 +37,7 @@ module TestProf
34
37
  }.freeze
35
38
 
36
39
  attr_accessor :instrumenter, :top_count, :per_example,
37
- :rank_by, :event
40
+ :rank_by, :event, :format
38
41
 
39
42
  def initialize
40
43
  @event = ENV["EVENT_PROF"]
@@ -42,6 +45,7 @@ module TestProf
42
45
  @top_count = (ENV["EVENT_PROF_TOP"] || 5).to_i
43
46
  @per_example = ENV["EVENT_PROF_EXAMPLES"] == "1"
44
47
  @rank_by = (ENV["EVENT_PROF_RANK"] || :time).to_sym
48
+ @format = ENV["EVENT_PROF_FORMAT"]
45
49
  @stamp = ENV["EVENT_PROF_STAMP"]
46
50
 
47
51
  RSpecStamp.config.tags = @stamp if stamp?
@@ -55,6 +59,13 @@ module TestProf
55
59
  per_example == true
56
60
  end
57
61
 
62
+ def printer
63
+ case format.to_s
64
+ when "json" then Printers::Json
65
+ else Printers::Simple
66
+ end
67
+ end
68
+
58
69
  def resolve_instrumenter
59
70
  return instrumenter if instrumenter.is_a?(Module)
60
71
 
@@ -3,22 +3,13 @@
3
3
  require "test_prof/before_all"
4
4
 
5
5
  Minitest.singleton_class.prepend(Module.new do
6
- attr_reader :previous_klass
6
+ attr_accessor :previous_klass
7
7
  @previous_klass = nil
8
+ end)
8
9
 
9
- # Minitest 6+
10
- if Minitest::Runnable.method_defined?(:run_suite)
11
- def run(klass, *rest)
12
- return super unless klass.respond_to?(:parallelized) && klass.parallelized
13
-
14
- if @previous_klass && @previous_klass != klass
15
- @previous_klass.before_all_executor&.deactivate!
16
- end
17
- @previous_klass = klass
18
-
19
- super
20
- end
21
- else
10
+ # Minitest 5
11
+ unless Minitest::Runnable.respond_to?(:run_suite)
12
+ Minitest.singleton_class.prepend(Module.new do
22
13
  def run_one_method(klass, *rest)
23
14
  return super unless klass.respond_to?(:parallelized) && klass.parallelized
24
15
 
@@ -29,8 +20,8 @@ Minitest.singleton_class.prepend(Module.new do
29
20
 
30
21
  super
31
22
  end
32
- end
33
- end)
23
+ end)
24
+ end
34
25
 
35
26
  module TestProf
36
27
  module BeforeAll
@@ -115,6 +106,24 @@ module TestProf
115
106
  base.extend ClassMethods
116
107
 
117
108
  base.cattr_accessor :parallelized
109
+ # Minitest 6+
110
+ if ::Minitest::Runnable.respond_to?(:run_suite)
111
+ base.prepend(Module.new do
112
+ def run(*)
113
+ klass = self.class
114
+ return super unless klass.parallelized
115
+
116
+ previous_klass = ::Minitest.previous_klass
117
+ if previous_klass && previous_klass != klass
118
+ previous_klass.before_all_executor&.deactivate!
119
+ end
120
+ ::Minitest.previous_klass = klass
121
+
122
+ super
123
+ end
124
+ end)
125
+ end
126
+
118
127
  if base.respond_to?(:parallelize_teardown)
119
128
  base.parallelize_teardown do
120
129
  last_klass = ::Minitest.previous_klass
@@ -126,7 +135,7 @@ module TestProf
126
135
 
127
136
  if base.respond_to?(:parallelize)
128
137
  base.singleton_class.prepend(Module.new do
129
- def parallelize(workers: :number_of_processors, with: :processes)
138
+ def parallelize(workers: :number_of_processors, with: :processes, **options)
130
139
  # super.parallelize returns nil when no parallelization is set up
131
140
  if super.nil?
132
141
  return
@@ -32,7 +32,7 @@ module TestProf # :nodoc: all
32
32
  end
33
33
 
34
34
  def print_group_result(group)
35
- "#{group[:desc].truncate} (#{group[:loc]}) – \e[1m#{group[:total_setup].duration}\e[22m " \
35
+ "#{group[:desc].truncate} (#{group[:loc]}) – #{bold(group[:total_setup].duration)} " \
36
36
  "of #{group[:total].duration} / #{group[:count]} " \
37
37
  "(before: #{(group[:total_setup] - group[:total_lazy_let]).duration}, " \
38
38
  "before let: #{group[:total_before_let].duration}, " \
@@ -53,6 +53,16 @@ module TestProf # :nodoc: all
53
53
 
54
54
  msgs.join
55
55
  end
56
+
57
+ private
58
+
59
+ # Follow the logger's color setting, so that a report written to a file
60
+ # or a pipe has no escape sequences in it
61
+ def bold(str)
62
+ return str unless TestProf.config.color?
63
+
64
+ "\e[1m#{str}\e[22m"
65
+ end
56
66
  end
57
67
  end
58
68
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TestProf
4
- VERSION = "1.6.3"
4
+ VERSION = "1.6.4"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: test-prof
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.3
4
+ version: 1.6.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-07-21 00:00:00.000000000 Z
11
+ date: 2026-10-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: logger
@@ -135,7 +135,6 @@ files:
135
135
  - config/default.yml
136
136
  - config/rubocop-rspec.yml
137
137
  - lib/minitest/base_reporter.rb
138
- - lib/minitest/event_prof_formatter.rb
139
138
  - lib/minitest/test_prof_plugin.rb
140
139
  - lib/rubocop/test_prof.rb
141
140
  - lib/rubocop/test_prof/cops/rspec/aggregate_examples.rb
@@ -167,6 +166,9 @@ files:
167
166
  - lib/test_prof/event_prof/instrumentations/active_support.rb
168
167
  - lib/test_prof/event_prof/minitest.rb
169
168
  - lib/test_prof/event_prof/monitor.rb
169
+ - lib/test_prof/event_prof/printers/base.rb
170
+ - lib/test_prof/event_prof/printers/json.rb
171
+ - lib/test_prof/event_prof/printers/simple.rb
170
172
  - lib/test_prof/event_prof/profiler.rb
171
173
  - lib/test_prof/event_prof/rspec.rb
172
174
  - lib/test_prof/ext/active_record_refind.rb
@@ -1,87 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "test_prof/ext/float_duration"
4
- require "test_prof/ext/string_truncate"
5
-
6
- module Minitest
7
- module TestProf
8
- class EventProfFormatter # :nodoc:
9
- using ::TestProf::FloatDuration
10
- using ::TestProf::StringTruncate
11
-
12
- def initialize(profilers)
13
- @profilers = profilers
14
- @results = []
15
- end
16
-
17
- def prepare_results
18
- @profilers.each do |profiler|
19
- total_results(profiler)
20
- by_groups(profiler)
21
- by_examples(profiler)
22
- end
23
- @results.join
24
- end
25
-
26
- private
27
-
28
- def total_results(profiler)
29
- time_percentage = time_percentage(profiler.total_time, profiler.absolute_run_time)
30
-
31
- @results <<
32
- <<~MSG
33
- EventProf results for #{profiler.event}
34
-
35
- Total time: #{profiler.total_time.duration} of #{profiler.absolute_run_time.duration} (#{time_percentage}%)
36
- Total events: #{profiler.total_count}
37
-
38
- Top #{profiler.top_count} slowest suites (by #{profiler.rank_by}):
39
-
40
- MSG
41
- end
42
-
43
- def by_groups(profiler)
44
- result = profiler.results
45
- groups = result[:groups]
46
-
47
- groups.each do |group|
48
- description = group[:id][:name]
49
- location = group[:id][:location]
50
- time = group[:time]
51
- run_time = group[:run_time]
52
- time_percentage = time_percentage(time, run_time)
53
-
54
- @results <<
55
- <<~GROUP
56
- #{description.truncate} (#{location}) – #{time.duration} (#{group[:count]} / #{group[:examples]}) of #{run_time.duration} (#{time_percentage}%)
57
- GROUP
58
- end
59
- end
60
-
61
- def by_examples(profiler)
62
- result = profiler.results
63
- examples = result[:examples]
64
-
65
- return unless examples
66
- @results << "\nTop #{profiler.top_count} slowest tests (by #{profiler.rank_by}):\n\n"
67
-
68
- examples.each do |example|
69
- description = example[:id][:name]
70
- location = example[:id][:location]
71
- time = example[:time]
72
- run_time = example[:run_time]
73
- time_percentage = time_percentage(time, run_time)
74
-
75
- @results <<
76
- <<~GROUP
77
- #{description.truncate} (#{location}) – #{time.duration} (#{example[:count]}) of #{run_time.duration} (#{time_percentage}%)
78
- GROUP
79
- end
80
- end
81
-
82
- def time_percentage(time, total_time)
83
- (time / total_time * 100).round(2)
84
- end
85
- end
86
- end
87
- end