test-prof 0.7.5 → 0.9.0

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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +174 -107
  3. data/LICENSE.txt +1 -1
  4. data/README.md +9 -3
  5. data/lib/minitest/base_reporter.rb +18 -12
  6. data/lib/minitest/test_prof_plugin.rb +8 -8
  7. data/lib/test_prof/any_fixture.rb +3 -3
  8. data/lib/test_prof/before_all/adapters/active_record.rb +11 -0
  9. data/lib/test_prof/before_all/isolator.rb +18 -0
  10. data/lib/test_prof/before_all.rb +82 -2
  11. data/lib/test_prof/cops/rspec/aggregate_failures.rb +6 -6
  12. data/lib/test_prof/event_prof/custom_events/factory_create.rb +27 -45
  13. data/lib/test_prof/event_prof/custom_events/sidekiq_inline.rb +8 -38
  14. data/lib/test_prof/event_prof/custom_events/sidekiq_jobs.rb +7 -25
  15. data/lib/test_prof/event_prof/instrumentations/active_support.rb +1 -1
  16. data/lib/test_prof/event_prof/minitest.rb +4 -4
  17. data/lib/test_prof/event_prof/monitor.rb +45 -8
  18. data/lib/test_prof/event_prof/rspec.rb +4 -11
  19. data/lib/test_prof/event_prof.rb +6 -6
  20. data/lib/test_prof/ext/factory_bot_strategy.rb +24 -0
  21. data/lib/test_prof/factory_bot.rb +1 -1
  22. data/lib/test_prof/factory_default.rb +1 -1
  23. data/lib/test_prof/factory_doctor/fabrication_patch.rb +12 -0
  24. data/lib/test_prof/factory_doctor/factory_bot_patch.rb +5 -1
  25. data/lib/test_prof/factory_doctor/minitest.rb +4 -4
  26. data/lib/test_prof/factory_doctor/rspec.rb +15 -12
  27. data/lib/test_prof/factory_doctor.rb +29 -7
  28. data/lib/test_prof/factory_prof/factory_builders/fabrication.rb +1 -1
  29. data/lib/test_prof/factory_prof/factory_builders/factory_bot.rb +2 -17
  30. data/lib/test_prof/factory_prof/printers/flamegraph.rb +6 -2
  31. data/lib/test_prof/factory_prof/printers/simple.rb +8 -6
  32. data/lib/test_prof/factory_prof.rb +29 -15
  33. data/lib/test_prof/recipes/logging.rb +86 -21
  34. data/lib/test_prof/recipes/minitest/before_all.rb +3 -2
  35. data/lib/test_prof/recipes/minitest/sample.rb +5 -5
  36. data/lib/test_prof/recipes/rspec/any_fixture.rb +3 -1
  37. data/lib/test_prof/recipes/rspec/before_all.rb +12 -3
  38. data/lib/test_prof/recipes/rspec/factory_all_stub.rb +5 -1
  39. data/lib/test_prof/recipes/rspec/let_it_be.rb +27 -12
  40. data/lib/test_prof/rspec_dissect/rspec.rb +2 -6
  41. data/lib/test_prof/rspec_dissect.rb +13 -19
  42. data/lib/test_prof/rspec_stamp/parser.rb +1 -1
  43. data/lib/test_prof/rspec_stamp/rspec.rb +1 -1
  44. data/lib/test_prof/rspec_stamp.rb +14 -14
  45. data/lib/test_prof/ruby_prof/rspec.rb +2 -6
  46. data/lib/test_prof/ruby_prof.rb +32 -24
  47. data/lib/test_prof/stack_prof/rspec.rb +5 -6
  48. data/lib/test_prof/stack_prof.rb +23 -15
  49. data/lib/test_prof/tag_prof/printers/simple.rb +4 -4
  50. data/lib/test_prof/tag_prof/result.rb +3 -3
  51. data/lib/test_prof/tag_prof/rspec.rb +9 -14
  52. data/lib/test_prof/utils/html_builder.rb +1 -1
  53. data/lib/test_prof/utils/sized_ordered_set.rb +1 -1
  54. data/lib/test_prof/version.rb +1 -1
  55. data/lib/test_prof.rb +6 -12
  56. metadata +35 -14
@@ -17,13 +17,89 @@ module TestProf
17
17
 
18
18
  def begin_transaction
19
19
  raise AdapterMissing if adapter.nil?
20
- adapter.begin_transaction
20
+
21
+ config.run_hooks(:begin) do
22
+ adapter.begin_transaction
23
+ end
24
+ yield
25
+ end
26
+
27
+ def within_transaction
28
+ yield
21
29
  end
22
30
 
23
31
  def rollback_transaction
24
32
  raise AdapterMissing if adapter.nil?
25
- adapter.rollback_transaction
33
+
34
+ config.run_hooks(:rollback) do
35
+ adapter.rollback_transaction
36
+ end
37
+ end
38
+
39
+ def config
40
+ @config ||= Configuration.new
41
+ end
42
+
43
+ def configure
44
+ yield config
45
+ end
46
+ end
47
+
48
+ class HooksChain # :nodoc:
49
+ attr_reader :type, :after, :before
50
+
51
+ def initialize(type)
52
+ @type = type
53
+ @before = []
54
+ @after = []
55
+ end
56
+
57
+ def run
58
+ before.each(&:call)
59
+ yield
60
+ after.each(&:call)
61
+ end
62
+ end
63
+
64
+ class Configuration
65
+ HOOKS = %i[begin rollback].freeze
66
+
67
+ def initialize
68
+ @hooks = Hash.new { |h, k| h[k] = HooksChain.new(k) }
69
+ end
70
+
71
+ # Add `before` hook for `begin` or
72
+ # `rollback` operation:
73
+ #
74
+ # config.before(:rollback) { ... }
75
+ def before(type)
76
+ validate_hook_type!(type)
77
+ hooks[type].before << Proc.new
78
+ end
79
+
80
+ # Add `after` hook for `begin` or
81
+ # `rollback` operation:
82
+ #
83
+ # config.after(:begin) { ... }
84
+ def after(type)
85
+ validate_hook_type!(type)
86
+ hooks[type].after << Proc.new
87
+ end
88
+
89
+ def run_hooks(type) # :nodoc:
90
+ validate_hook_type!(type)
91
+ hooks[type].run { yield }
92
+ end
93
+
94
+ private
95
+
96
+ def validate_hook_type!(type)
97
+ return if HOOKS.include?(type)
98
+
99
+ raise ArgumentError, "Unknown hook type: #{type}. Valid types: #{HOOKS.join(", ")}"
26
100
  end
101
+
102
+ attr_reader :hooks
27
103
  end
28
104
  end
29
105
  end
@@ -33,3 +109,7 @@ if defined?(::ActiveRecord::Base)
33
109
 
34
110
  TestProf::BeforeAll.adapter = TestProf::BeforeAll::Adapters::ActiveRecord
35
111
  end
112
+
113
+ if defined?(::Isolator)
114
+ require "test_prof/before_all/isolator"
115
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rubocop'
4
- require 'test_prof/utils'
3
+ require "rubocop"
4
+ require "test_prof/utils"
5
5
 
6
6
  module RuboCop
7
7
  module Cop
@@ -38,7 +38,7 @@ module RuboCop
38
38
  class << self
39
39
  def supported?
40
40
  return @supported if instance_variable_defined?(:@supported)
41
- @supported = TestProf::Utils.verify_gem_version('rubocop', at_least: '0.51.0')
41
+ @supported = TestProf::Utils.verify_gem_version("rubocop", at_least: "0.51.0")
42
42
 
43
43
  unless @supported
44
44
  warn "RSpec/AggregateFailures cop requires RuboCop >= 0.51.0. Skipping"
@@ -62,7 +62,7 @@ module RuboCop
62
62
  add_offense(
63
63
  node,
64
64
  location: :expression,
65
- message: 'Use :aggregate_failures instead of several one-liners.'
65
+ message: "Use :aggregate_failures instead of several one-liners."
66
66
  )
67
67
  end
68
68
 
@@ -140,7 +140,7 @@ module RuboCop
140
140
  %(#{method_name} "works", :aggregate_failures do)
141
141
  end
142
142
 
143
- def body_from(node, base_indent = '')
143
+ def body_from(node, base_indent = "")
144
144
  method, _args, body = *node
145
145
  body_source = method.method_name == :its ? body_from_its(method, body) : body.source
146
146
  "#{base_indent}#{indent}#{body_source}"
@@ -162,7 +162,7 @@ module RuboCop
162
162
  end
163
163
 
164
164
  def indent
165
- @indent ||= " " * (config.for_cop('IndentationWidth')['Width'] || 2)
165
+ @indent ||= " " * (config.for_cop("IndentationWidth")["Width"] || 2)
166
166
  end
167
167
  end
168
168
  end
@@ -1,56 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "test_prof/factory_bot"
4
+ require "test_prof/ext/factory_bot_strategy"
4
5
 
5
- module TestProf::EventProf::CustomEvents
6
- module FactoryCreate # :nodoc: all
7
- module RunnerPatch
8
- def run(strategy = @strategy)
9
- return super unless strategy == :create
10
- FactoryCreate.track(@name) do
11
- super
12
- end
13
- end
14
- end
15
-
16
- class << self
17
- def setup!
18
- @depth = 0
19
- TestProf::FactoryBot::FactoryRunner.prepend RunnerPatch
20
- end
6
+ using TestProf::FactoryBotStrategy
21
7
 
22
- def track(factory)
23
- @depth += 1
24
- res = nil
25
- begin
26
- res =
27
- if @depth == 1
28
- ActiveSupport::Notifications.instrument(
29
- 'factory.create',
30
- name: factory
31
- ) { yield }
32
- else
33
- yield
34
- end
35
- ensure
36
- @depth -= 1
37
- end
38
- res
39
- end
8
+ TestProf::EventProf::CustomEvents.register("factory.create") do
9
+ if defined?(TestProf::FactoryBot) || defined?(Fabricate)
10
+ if defined?(TestProf::FactoryBot)
11
+ TestProf::EventProf.monitor(
12
+ TestProf::FactoryBot::FactoryRunner,
13
+ "factory.create",
14
+ :run,
15
+ top_level: true,
16
+ guard: ->(strategy = @strategy) { strategy.create? }
17
+ )
40
18
  end
41
- end
42
- end
43
19
 
44
- TestProf::EventProf::CustomEvents.register("factory.create") do
45
- if defined? TestProf::FactoryBot
46
- TestProf::EventProf::CustomEvents::FactoryCreate.setup!
20
+ if defined?(Fabricate)
21
+ TestProf::EventProf.monitor(
22
+ Fabricate.singleton_class,
23
+ "factory.create",
24
+ :create,
25
+ top_level: true
26
+ )
27
+ end
47
28
  else
48
- TestProf.log(:error,
49
- <<~MSG
50
- Failed to load factory_bot / factory_girl.
29
+ TestProf.log(
30
+ :error,
31
+ <<~MSG
32
+ Failed to load factory_bot / factory_girl / fabrication.
51
33
 
52
- Make sure that any of them is in your Gemfile.
53
- MSG
54
- )
34
+ Make sure that any of them is in your Gemfile.
35
+ MSG
36
+ )
55
37
  end
56
38
  end
@@ -1,50 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module TestProf::EventProf::CustomEvents
4
- module SidekiqInline # :nodoc: all
5
- module ClientPatch
6
- def raw_push(*)
7
- return super unless Sidekiq::Testing.inline?
8
- SidekiqInline.track { super }
9
- end
10
- end
11
-
12
- class << self
13
- def setup!
14
- @depth = 0
15
- Sidekiq::Client.prepend ClientPatch
16
- end
17
-
18
- def track
19
- @depth += 1
20
- res = nil
21
- begin
22
- res =
23
- if @depth == 1
24
- ActiveSupport::Notifications.instrument(
25
- 'sidekiq.inline'
26
- ) { yield }
27
- else
28
- yield
29
- end
30
- ensure
31
- @depth -= 1
32
- end
33
- res
34
- end
35
- end
36
- end
37
- end
38
-
39
3
  TestProf::EventProf::CustomEvents.register("sidekiq.inline") do
40
4
  if TestProf.require(
41
- 'sidekiq/testing',
5
+ "sidekiq/testing",
42
6
  <<~MSG
43
7
  Failed to load Sidekiq.
44
8
 
45
9
  Make sure that "sidekiq" gem is in your Gemfile.
46
10
  MSG
47
11
  )
48
- TestProf::EventProf::CustomEvents::SidekiqInline.setup!
12
+ TestProf::EventProf.monitor(
13
+ Sidekiq::Client,
14
+ "sidekiq.inline",
15
+ :raw_push,
16
+ top_level: true,
17
+ guard: ->(*) { Sidekiq::Testing.inline? }
18
+ )
49
19
  end
50
20
  end
@@ -1,38 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module TestProf::EventProf::CustomEvents
4
- module SidekiqJobs # :nodoc: all
5
- module ClientPatch
6
- def raw_push(*)
7
- return super unless Sidekiq::Testing.inline?
8
- SidekiqJobs.track { super }
9
- end
10
- end
11
-
12
- class << self
13
- def setup!
14
- Sidekiq::Client.prepend ClientPatch
15
- end
16
-
17
- def track
18
- ActiveSupport::Notifications.instrument(
19
- 'sidekiq.jobs'
20
- ) { yield }
21
- end
22
- end
23
- end
24
- end
25
-
26
3
  TestProf::EventProf::CustomEvents.register("sidekiq.jobs") do
27
4
  if TestProf.require(
28
- 'sidekiq/testing',
5
+ "sidekiq/testing",
29
6
  <<~MSG
30
7
  Failed to load Sidekiq.
31
8
 
32
9
  Make sure that "sidekiq" gem is in your Gemfile.
33
10
  MSG
34
11
  )
35
- TestProf::EventProf::CustomEvents::SidekiqJobs.setup!
12
+ TestProf::EventProf.monitor(
13
+ Sidekiq::Client,
14
+ "sidekiq.jobs",
15
+ :raw_push,
16
+ guard: ->(*) { Sidekiq::Testing.inline? }
17
+ )
36
18
  TestProf::EventProf.configure do |config|
37
19
  config.rank_by = :count
38
20
  end
@@ -6,7 +6,7 @@ module TestProf::EventProf
6
6
  module ActiveSupport
7
7
  class << self
8
8
  def subscribe(event)
9
- raise ArgumentError, 'Block is required!' unless block_given?
9
+ raise ArgumentError, "Block is required!" unless block_given?
10
10
 
11
11
  ::ActiveSupport::Notifications.subscribe(event) do |_event, start, finish, *_args|
12
12
  yield (finish - start)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'minitest/base_reporter'
4
- require 'minitest/event_prof_formatter'
3
+ require "minitest/base_reporter"
4
+ require "minitest/event_prof_formatter"
5
5
 
6
6
  module Minitest
7
7
  module TestProf
@@ -10,7 +10,7 @@ module Minitest
10
10
  super
11
11
  @profiler = configure_profiler(options)
12
12
 
13
- log :info, "EventProf enabled (#{@profiler.events.join(', ')})"
13
+ log :info, "EventProf enabled (#{@profiler.events.join(", ")})"
14
14
 
15
15
  @formatter = EventProfFormatter.new(@profiler)
16
16
  @current_group = nil
@@ -45,7 +45,7 @@ module Minitest
45
45
  end
46
46
 
47
47
  @current_example = {
48
- name: example.gsub(/^test_(?:\d+_)?/, ''),
48
+ name: example.gsub(/^test_(?:\d+_)?/, ""),
49
49
  location: location_with_line_number(group, example)
50
50
  }
51
51
 
@@ -4,17 +4,54 @@ module TestProf
4
4
  module EventProf
5
5
  # Wrap methods with instrumentation
6
6
  module Monitor
7
+ class BaseTracker
8
+ attr_reader :event
9
+
10
+ def initialize(event)
11
+ @event = event
12
+ end
13
+
14
+ def track
15
+ TestProf::EventProf.instrumenter.instrument(event) { yield }
16
+ end
17
+ end
18
+
19
+ class TopLevelTracker < BaseTracker
20
+ attr_reader :id
21
+
22
+ def initialize(event)
23
+ super
24
+ @id = :"event_prof_monitor_#{event}"
25
+ Thread.current[id] = 0
26
+ end
27
+
28
+ def track
29
+ Thread.current[id] += 1
30
+ res = nil
31
+ begin
32
+ res =
33
+ if Thread.current[id] == 1
34
+ super { yield }
35
+ else
36
+ yield
37
+ end
38
+ ensure
39
+ Thread.current[id] -= 1
40
+ end
41
+ res
42
+ end
43
+ end
44
+
7
45
  class << self
8
- def call(mod, event, *mids)
46
+ def call(mod, event, *mids, guard: nil, top_level: false)
47
+ tracker = top_level ? TopLevelTracker.new(event) : BaseTracker.new(event)
48
+
9
49
  patch = Module.new do
10
50
  mids.each do |mid|
11
- module_eval <<~SRC, __FILE__, __LINE__ + 1
12
- def #{mid}(*)
13
- TestProf::EventProf.instrumenter.instrument(
14
- '#{event}'
15
- ) { super }
16
- end
17
- SRC
51
+ define_method(mid) do |*args, &block|
52
+ next super(*args, &block) unless guard.nil? || instance_exec(*args, &guard)
53
+ tracker.track { super(*args, &block) }
54
+ end
18
55
  end
19
56
  end
20
57
 
@@ -14,15 +14,13 @@ module TestProf
14
14
  example_group_started
15
15
  example_group_finished
16
16
  example_started
17
- example_failed
18
- example_passed
19
- example_pending
17
+ example_finished
20
18
  ].freeze
21
19
 
22
20
  def initialize
23
21
  @profiler = EventProf.build
24
22
 
25
- log :info, "EventProf enabled (#{@profiler.events.join(', ')})"
23
+ log :info, "EventProf enabled (#{@profiler.events.join(", ")})"
26
24
  end
27
25
 
28
26
  def example_group_started(notification)
@@ -43,11 +41,6 @@ module TestProf
43
41
  @profiler.example_finished notification.example
44
42
  end
45
43
 
46
- # NOTE: RSpec < 3.4.0 doesn't have example_finished event
47
- alias example_passed example_finished
48
- alias example_failed example_finished
49
- alias example_pending example_finished
50
-
51
44
  def print
52
45
  @profiler.each(&method(:report))
53
46
  end
@@ -145,8 +138,8 @@ module TestProf
145
138
  end
146
139
 
147
140
  # Register EventProf listener
148
- TestProf.activate('EVENT_PROF') do
149
- TestProf::EventProf::CustomEvents.activate_all(ENV['EVENT_PROF'])
141
+ TestProf.activate("EVENT_PROF") do
142
+ TestProf::EventProf::CustomEvents.activate_all(ENV["EVENT_PROF"])
150
143
 
151
144
  RSpec.configure do |config|
152
145
  listener = nil
@@ -31,19 +31,19 @@ module TestProf
31
31
  class Configuration
32
32
  # Map of supported instrumenters
33
33
  INSTRUMENTERS = {
34
- active_support: 'ActiveSupport'
34
+ active_support: "ActiveSupport"
35
35
  }.freeze
36
36
 
37
37
  attr_accessor :instrumenter, :top_count, :per_example,
38
38
  :rank_by, :event
39
39
 
40
40
  def initialize
41
- @event = ENV['EVENT_PROF']
41
+ @event = ENV["EVENT_PROF"]
42
42
  @instrumenter = :active_support
43
- @top_count = (ENV['EVENT_PROF_TOP'] || 5).to_i
44
- @per_example = ENV['EVENT_PROF_EXAMPLES'] == '1'
45
- @rank_by = (ENV['EVENT_PROF_RANK'] || :time).to_sym
46
- @stamp = ENV['EVENT_PROF_STAMP']
43
+ @top_count = (ENV["EVENT_PROF_TOP"] || 5).to_i
44
+ @per_example = ENV["EVENT_PROF_EXAMPLES"] == "1"
45
+ @rank_by = (ENV["EVENT_PROF_RANK"] || :time).to_sym
46
+ @stamp = ENV["EVENT_PROF_STAMP"]
47
47
 
48
48
  RSpecStamp.config.tags = @stamp if stamp?
49
49
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TestProf
4
+ # FactoryBot 5.0 uses strategy classes for associations,
5
+ # older versions and top-level invocations use Symbols.
6
+ #
7
+ # This Refinement should be used FactoryRunner patches to check
8
+ # that strategy is :create.
9
+ module FactoryBotStrategy
10
+ refine Symbol do
11
+ def create?
12
+ self == :create
13
+ end
14
+ end
15
+
16
+ if defined?(::FactoryBot::Strategy::Create)
17
+ refine Class do
18
+ def create?
19
+ self <= ::FactoryBot::Strategy::Create
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TestProf # :nodoc: all
4
- FACTORY_GIRL_NAMES = { 'factory_bot' => '::FactoryBot', 'factory_girl' => '::FactoryGirl' }.freeze
4
+ FACTORY_GIRL_NAMES = {"factory_bot" => "::FactoryBot", "factory_girl" => "::FactoryGirl"}.freeze
5
5
 
6
6
  FACTORY_GIRL_NAMES.find do |name, cname|
7
7
  TestProf.require(name) do
@@ -38,7 +38,7 @@ module TestProf
38
38
 
39
39
  def register(name, obj, **options)
40
40
  options[:preserve_traits] = true if FactoryDefault.preserve_traits
41
- store[name] = { object: obj, **options }
41
+ store[name] = {object: obj, **options}
42
42
  obj
43
43
  end
44
44
 
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TestProf
4
+ module FactoryDoctor
5
+ # Wrap #run method with FactoryDoctor tracking
6
+ module FabricationPatch
7
+ def create(*)
8
+ FactoryDoctor.within_factory(:create) { super }
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,11 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "test_prof/ext/factory_bot_strategy"
4
+
3
5
  module TestProf
4
6
  module FactoryDoctor
5
7
  # Wrap #run method with FactoryDoctor tracking
6
8
  module FactoryBotPatch
9
+ using TestProf::FactoryBotStrategy
10
+
7
11
  def run(strategy = @strategy)
8
- FactoryDoctor.within_factory(strategy) { super }
12
+ FactoryDoctor.within_factory(strategy.create? ? :create : :other) { super }
9
13
  end
10
14
  end
11
15
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'minitest/base_reporter'
4
- require 'test_prof/ext/float_duration'
3
+ require "minitest/base_reporter"
4
+ require "test_prof/ext/float_duration"
5
5
 
6
6
  module Minitest
7
7
  module TestProf # :nodoc:
@@ -47,7 +47,7 @@ module Minitest
47
47
  }
48
48
 
49
49
  @example_groups[group] << {
50
- description: example.name.gsub(/^test_(?:\d+_)?/, ''),
50
+ description: example.name.gsub(/^test_(?:\d+_)?/, ""),
51
51
  location: location_with_line_number(example),
52
52
  factories: result.count,
53
53
  time: result.time
@@ -87,7 +87,7 @@ module Minitest
87
87
  private
88
88
 
89
89
  def pluralize_records(count)
90
- count == 1 ? '1 record' : "#{count} records"
90
+ count == 1 ? "1 record" : "#{count} records"
91
91
  end
92
92
  end
93
93
  end
@@ -12,9 +12,7 @@ module TestProf
12
12
 
13
13
  NOTIFICATIONS = %i[
14
14
  example_started
15
- example_passed
16
- example_failed
17
- example_pending
15
+ example_finished
18
16
  ].freeze
19
17
 
20
18
  def initialize
@@ -45,11 +43,6 @@ module TestProf
45
43
  @time += result.time
46
44
  end
47
45
 
48
- # NOTE: RSpec < 3.4.0 doesn't have example_finished event
49
- alias example_passed example_finished
50
- alias example_failed example_finished
51
- alias example_pending example_finished
52
-
53
46
  def print
54
47
  return log(:info, SUCCESS_MESSAGE) if @example_groups.empty?
55
48
 
@@ -65,9 +58,15 @@ module TestProf
65
58
  MSG
66
59
 
67
60
  @example_groups.each do |group, examples|
68
- msgs << "#{group.description} (#{group.metadata[:location]})\n"
61
+ group_time = examples.sum { |ex| ex.metadata[:time] }
62
+ group_count = examples.sum { |ex| ex.metadata[:factories] }
63
+
64
+ msgs << "#{group.description} (#{group.metadata[:location]}) " \
65
+ "(#{pluralize_records(group_count)} created, " \
66
+ "#{group_time.duration})\n"
67
+
69
68
  examples.each do |ex|
70
- msgs << " #{ex.description} (#{ex.metadata[:location]}) "\
69
+ msgs << " #{ex.description} (#{ex.metadata[:location]}) " \
71
70
  "– #{pluralize_records(ex.metadata[:factories])} created, "\
72
71
  "#{ex.metadata[:time].duration}\n"
73
72
  end
@@ -122,7 +121,7 @@ module TestProf
122
121
  end
123
122
 
124
123
  # Register FactoryDoctor listener
125
- TestProf.activate('FDOC') do
124
+ TestProf.activate("FDOC") do
126
125
  TestProf::FactoryDoctor.init
127
126
 
128
127
  RSpec.configure do |config|
@@ -138,7 +137,11 @@ TestProf.activate('FDOC') do
138
137
  config.after(:suite) { listener&.print }
139
138
  end
140
139
 
141
- RSpec.shared_context "factory_doctor:ignore", fd_ignore: true do
140
+ RSpec.shared_context "factory_doctor:ignore" do
142
141
  around(:each) { |ex| TestProf::FactoryDoctor.ignore(&ex) }
143
142
  end
143
+
144
+ RSpec.configure do |config|
145
+ config.include_context "factory_doctor:ignore", fd_ignore: true
146
+ end
144
147
  end