test-prof 0.7.5 → 0.8.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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -2
  3. data/README.md +5 -3
  4. data/lib/minitest/base_reporter.rb +18 -12
  5. data/lib/minitest/test_prof_plugin.rb +8 -8
  6. data/lib/test_prof.rb +3 -3
  7. data/lib/test_prof/any_fixture.rb +3 -3
  8. data/lib/test_prof/before_all.rb +9 -0
  9. data/lib/test_prof/before_all/adapters/active_record.rb +12 -0
  10. data/lib/test_prof/before_all/isolator.rb +18 -0
  11. data/lib/test_prof/cops/rspec/aggregate_failures.rb +6 -6
  12. data/lib/test_prof/event_prof.rb +6 -6
  13. data/lib/test_prof/event_prof/custom_events/factory_create.rb +1 -1
  14. data/lib/test_prof/event_prof/custom_events/sidekiq_inline.rb +2 -2
  15. data/lib/test_prof/event_prof/custom_events/sidekiq_jobs.rb +2 -2
  16. data/lib/test_prof/event_prof/instrumentations/active_support.rb +1 -1
  17. data/lib/test_prof/event_prof/minitest.rb +4 -4
  18. data/lib/test_prof/event_prof/rspec.rb +4 -11
  19. data/lib/test_prof/factory_bot.rb +1 -1
  20. data/lib/test_prof/factory_default.rb +1 -1
  21. data/lib/test_prof/factory_doctor.rb +2 -2
  22. data/lib/test_prof/factory_doctor/minitest.rb +4 -4
  23. data/lib/test_prof/factory_doctor/rspec.rb +7 -10
  24. data/lib/test_prof/factory_prof.rb +6 -6
  25. data/lib/test_prof/factory_prof/factory_builders/fabrication.rb +1 -1
  26. data/lib/test_prof/factory_prof/printers/flamegraph.rb +1 -1
  27. data/lib/test_prof/recipes/logging.rb +86 -21
  28. data/lib/test_prof/recipes/minitest/before_all.rb +3 -2
  29. data/lib/test_prof/recipes/minitest/sample.rb +5 -5
  30. data/lib/test_prof/recipes/rspec/any_fixture.rb +3 -1
  31. data/lib/test_prof/recipes/rspec/before_all.rb +12 -3
  32. data/lib/test_prof/recipes/rspec/factory_all_stub.rb +5 -1
  33. data/lib/test_prof/recipes/rspec/let_it_be.rb +3 -13
  34. data/lib/test_prof/rspec_dissect.rb +13 -19
  35. data/lib/test_prof/rspec_dissect/rspec.rb +2 -6
  36. data/lib/test_prof/rspec_stamp.rb +14 -14
  37. data/lib/test_prof/rspec_stamp/parser.rb +1 -1
  38. data/lib/test_prof/rspec_stamp/rspec.rb +1 -1
  39. data/lib/test_prof/ruby_prof.rb +32 -24
  40. data/lib/test_prof/ruby_prof/rspec.rb +2 -6
  41. data/lib/test_prof/stack_prof.rb +23 -15
  42. data/lib/test_prof/stack_prof/rspec.rb +5 -6
  43. data/lib/test_prof/tag_prof/printers/simple.rb +4 -4
  44. data/lib/test_prof/tag_prof/result.rb +3 -3
  45. data/lib/test_prof/tag_prof/rspec.rb +9 -14
  46. data/lib/test_prof/utils/html_builder.rb +1 -1
  47. data/lib/test_prof/utils/sized_ordered_set.rb +1 -1
  48. data/lib/test_prof/version.rb +1 -1
  49. metadata +37 -9
@@ -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
@@ -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
 
@@ -45,7 +45,7 @@ module TestProf
45
45
  attr_reader :count, :time, :queries_count
46
46
 
47
47
  # Patch factory lib, init counters
48
- def init(event = 'sql.active_record')
48
+ def init(event = "sql.active_record")
49
49
  @event = event
50
50
  reset!
51
51
 
@@ -57,7 +57,7 @@ module TestProf
57
57
 
58
58
  subscribe!
59
59
 
60
- @stamp = ENV['FDOC_STAMP']
60
+ @stamp = ENV["FDOC_STAMP"]
61
61
 
62
62
  RSpecStamp.config.tags = @stamp if stamp?
63
63
  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
 
@@ -122,7 +115,7 @@ module TestProf
122
115
  end
123
116
 
124
117
  # Register FactoryDoctor listener
125
- TestProf.activate('FDOC') do
118
+ TestProf.activate("FDOC") do
126
119
  TestProf::FactoryDoctor.init
127
120
 
128
121
  RSpec.configure do |config|
@@ -138,7 +131,11 @@ TestProf.activate('FDOC') do
138
131
  config.after(:suite) { listener&.print }
139
132
  end
140
133
 
141
- RSpec.shared_context "factory_doctor:ignore", fd_ignore: true do
134
+ RSpec.shared_context "factory_doctor:ignore" do
142
135
  around(:each) { |ex| TestProf::FactoryDoctor.ignore(&ex) }
143
136
  end
137
+
138
+ RSpec.configure do |config|
139
+ config.include_context "factory_doctor:ignore", fd_ignore: true
140
+ end
144
141
  end
@@ -17,7 +17,7 @@ module TestProf
17
17
  attr_accessor :mode
18
18
 
19
19
  def initialize
20
- @mode = ENV['FPROF'] == 'flamegraph' ? :flamegraph : :simple
20
+ @mode = ENV["FPROF"] == "flamegraph" ? :flamegraph : :simple
21
21
  end
22
22
 
23
23
  # Whether we want to generate flamegraphs
@@ -39,7 +39,7 @@ module TestProf
39
39
  return @stats if instance_variable_defined?(:@stats)
40
40
 
41
41
  @stats = @raw_stats.values
42
- .sort_by { |el| -el[:total] }
42
+ .sort_by { |el| -el[:total] }
43
43
  end
44
44
 
45
45
  def total
@@ -51,8 +51,8 @@ module TestProf
51
51
 
52
52
  def sorted_stats(key)
53
53
  @raw_stats.values
54
- .map { |el| [el[:name], el[key]] }
55
- .sort_by { |el| -el[1] }
54
+ .map { |el| [el[:name], el[key]] }
55
+ .sort_by { |el| -el[1] }
56
56
  end
57
57
  end
58
58
 
@@ -120,7 +120,7 @@ module TestProf
120
120
  def reset!
121
121
  @stacks = [] if config.flamegraph?
122
122
  @depth = 0
123
- @stats = Hash.new { |h, k| h[k] = { name: k, total: 0, top_level: 0 } }
123
+ @stats = Hash.new { |h, k| h[k] = {name: k, total: 0, top_level: 0} }
124
124
  flush_stack
125
125
  end
126
126
 
@@ -137,6 +137,6 @@ module TestProf
137
137
  end
138
138
  end
139
139
 
140
- TestProf.activate('FPROF') do
140
+ TestProf.activate("FPROF") do
141
141
  TestProf::FactoryProf.run
142
142
  end
@@ -10,7 +10,7 @@ module TestProf
10
10
  class Fabrication
11
11
  # Monkey-patch Fabrication
12
12
  def self.patch
13
- TestProf.require 'fabrication' do
13
+ TestProf.require "fabrication" do
14
14
  ::Fabricate.singleton_class.prepend(FabricationPatch)
15
15
  end
16
16
  end
@@ -45,7 +45,7 @@ module TestProf::FactoryProf
45
45
  node = paths[path]
46
46
  node[:value] += 1
47
47
  else
48
- node = { name: sample, value: 1, total: result.raw_stats.fetch(sample)[:total] }
48
+ node = {name: sample, value: 1, total: result.raw_stats.fetch(sample)[:total]}
49
49
  paths[path] = node
50
50
 
51
51
  if parent.nil?
@@ -2,44 +2,109 @@
2
2
 
3
3
  require "test_prof"
4
4
 
5
+ module TestProf
6
+ module Rails
7
+ # Add `with_logging` and `with_ar_logging helpers`
8
+ module LoggingHelpers
9
+ class << self
10
+ attr_writer :logger
11
+
12
+ def logger
13
+ return @logger if instance_variable_defined?(:@logger)
14
+
15
+ @logger = Logger.new(STDOUT)
16
+ end
17
+
18
+ def ar_loggables
19
+ return @ar_loggables if instance_variable_defined?(:@ar_loggables)
20
+
21
+ @ar_loggables = [
22
+ ::ActiveRecord::Base,
23
+ ::ActiveSupport::LogSubscriber
24
+ ]
25
+ end
26
+
27
+ # rubocop:disable Metrics/CyclomaticComplexity
28
+ # rubocop:disable Metrics/PerceivedComplexity
29
+ def all_loggables
30
+ return @all_loggables if instance_variable_defined?(:@all_loggables)
31
+
32
+ @all_loggables = [
33
+ ::ActiveSupport::LogSubscriber,
34
+ ::Rails,
35
+ defined?(::ActiveRecord::Base) && ::ActiveRecord::Base,
36
+ defined?(::ActiveJob::Base) && ::ActiveJob::Base,
37
+ defined?(::ActionView::Base) && ::ActionView::Base,
38
+ defined?(::ActionMailer::Base) && ::ActionMailer::Base,
39
+ defined?(::ActionCable::Server::Base.config) && ::ActionCable::Server::Base.config,
40
+ defined?(::ActiveStorage) && ::ActiveStorage
41
+ ].compact
42
+ end
43
+ # rubocop:enable Metrics/CyclomaticComplexity
44
+ # rubocop:enable Metrics/PerceivedComplexity
45
+
46
+ def swap_logger(loggables)
47
+ loggables.map do |loggable|
48
+ was_logger = loggable.logger
49
+ loggable.logger = logger
50
+ was_logger
51
+ end
52
+ end
53
+
54
+ def restore_logger(was_loggers, loggables)
55
+ loggables.each_with_index do |loggable, i|
56
+ loggable.logger = was_loggers[i]
57
+ end
58
+ end
59
+ end
60
+
61
+ # Enable verbose Rails logging within a block
62
+ def with_logging
63
+ *loggers = LoggingHelpers.swap_logger(LoggingHelpers.all_loggables)
64
+ yield
65
+ ensure
66
+ LoggingHelpers.restore_logger(loggers, LoggingHelpers.all_loggables)
67
+ end
68
+
69
+ def with_ar_logging
70
+ *loggers = LoggingHelpers.swap_logger(LoggingHelpers.ar_loggables)
71
+ yield
72
+ ensure
73
+ LoggingHelpers.restore_logger(loggers, LoggingHelpers.ar_loggables)
74
+ end
75
+ end
76
+ end
77
+ end
78
+
5
79
  if TestProf.rspec?
6
- RSpec.shared_context "logging:verbose", log: true do
80
+ RSpec.shared_context "logging:verbose" do
7
81
  around(:each) do |ex|
8
- *loggers = ActiveSupport::LogSubscriber.logger,
9
- Rails.logger,
10
- ActiveRecord::Base.logger
11
- ActiveSupport::LogSubscriber.logger =
12
- Rails.logger =
13
- ActiveRecord::Base.logger = Logger.new(STDOUT)
14
- ex.run
15
- ActiveSupport::LogSubscriber.logger,
16
- Rails.logger,
17
- ActiveRecord::Base.logger = *loggers
82
+ with_logging(&ex)
18
83
  end
19
84
  end
20
85
 
21
- RSpec.shared_context "logging:active_record", log: :ar do
86
+ RSpec.shared_context "logging:active_record" do
22
87
  around(:each) do |ex|
23
- *loggers = ActiveRecord::Base.logger,
24
- ActiveSupport::LogSubscriber.logger
25
- ActiveSupport::LogSubscriber.logger =
26
- ActiveRecord::Base.logger = Logger.new(STDOUT)
27
- ex.run
28
- ActiveSupport::LogSubscriber.logger,
29
- ActiveRecord::Base.logger = *loggers
88
+ with_ar_logging(&ex)
30
89
  end
31
90
  end
91
+
92
+ RSpec.configure do |config|
93
+ config.include TestProf::Rails::LoggingHelpers
94
+ config.include_context "logging:active_record", log: :ar
95
+ config.include_context "logging:verbose", log: true
96
+ end
32
97
  end
33
98
 
34
99
  TestProf.activate("LOG", "all") do
35
100
  TestProf.log :info, "Rails verbose logging enabled"
36
101
  ActiveSupport::LogSubscriber.logger =
37
102
  Rails.logger =
38
- ActiveRecord::Base.logger = Logger.new(STDOUT)
103
+ ActiveRecord::Base.logger = TestProf::Rails::LoggingHelpers.logger
39
104
  end
40
105
 
41
106
  TestProf.activate("LOG", "ar") do
42
107
  TestProf.log :info, "Active Record verbose logging enabled"
43
108
  ActiveSupport::LogSubscriber.logger =
44
- ActiveRecord::Base.logger = Logger.new(STDOUT)
109
+ ActiveRecord::Base.logger = TestProf::Rails::LoggingHelpers.logger
45
110
  end
@@ -20,8 +20,9 @@ module TestProf
20
20
  return if active?
21
21
  @active = true
22
22
  @examples_left = test_class.runnable_methods.size
23
- BeforeAll.begin_transaction
24
- capture!
23
+ BeforeAll.begin_transaction do
24
+ capture!
25
+ end
25
26
  end
26
27
 
27
28
  def try_deactivate!
@@ -14,7 +14,7 @@ module TestProf
14
14
  def suites
15
15
  # Make sure that sample contains only _real_ suites
16
16
  Minitest::Runnable.runnables
17
- .reject { |suite| CORE_RUNNABLES.include?(suite) }
17
+ .reject { |suite| CORE_RUNNABLES.include?(suite) }
18
18
  end
19
19
 
20
20
  def sample_groups(sample_size)
@@ -39,10 +39,10 @@ module TestProf
39
39
 
40
40
  # Overrides Minitest.run
41
41
  def run(*)
42
- if ENV['SAMPLE']
43
- MinitestSample.sample_examples(ENV['SAMPLE'].to_i)
44
- elsif ENV['SAMPLE_GROUPS']
45
- MinitestSample.sample_groups(ENV['SAMPLE_GROUPS'].to_i)
42
+ if ENV["SAMPLE"]
43
+ MinitestSample.sample_examples(ENV["SAMPLE"].to_i)
44
+ elsif ENV["SAMPLE_GROUPS"]
45
+ MinitestSample.sample_groups(ENV["SAMPLE_GROUPS"].to_i)
46
46
  end
47
47
  super
48
48
  end
@@ -3,7 +3,7 @@
3
3
  require "test_prof/any_fixture"
4
4
  require "test_prof/recipes/rspec/before_all"
5
5
 
6
- RSpec.shared_context "any_fixture:clean", with_clean_fixture: true do
6
+ RSpec.shared_context "any_fixture:clean" do
7
7
  extend TestProf::BeforeAll::RSpec
8
8
 
9
9
  before_all do
@@ -12,6 +12,8 @@ RSpec.shared_context "any_fixture:clean", with_clean_fixture: true do
12
12
  end
13
13
 
14
14
  RSpec.configure do |config|
15
+ config.include_context "any_fixture:clean", with_clean_fixture: true
16
+
15
17
  config.after(:suite) do
16
18
  TestProf::AnyFixture.report_stats if TestProf::AnyFixture.reporting_enabled?
17
19
  TestProf::AnyFixture.reset
@@ -9,13 +9,14 @@ module TestProf
9
9
  def before_all(&block)
10
10
  raise ArgumentError, "Block is required!" unless block_given?
11
11
 
12
- return before(:all, &block) if within_before_all?
12
+ return within_before_all(&block) if within_before_all?
13
13
 
14
14
  @__before_all_activated__ = true
15
15
 
16
16
  before(:all) do
17
- BeforeAll.begin_transaction
18
- instance_eval(&block)
17
+ BeforeAll.begin_transaction do
18
+ instance_eval(&block)
19
+ end
19
20
  end
20
21
 
21
22
  after(:all) do
@@ -23,6 +24,14 @@ module TestProf
23
24
  end
24
25
  end
25
26
 
27
+ def within_before_all(&block)
28
+ before(:all) do
29
+ BeforeAll.within_transaction do
30
+ instance_eval(&block)
31
+ end
32
+ end
33
+ end
34
+
26
35
  def within_before_all?
27
36
  instance_variable_defined?(:@__before_all_activated__)
28
37
  end
@@ -4,7 +4,11 @@ require "test_prof/factory_all_stub"
4
4
 
5
5
  TestProf::FactoryAllStub.init
6
6
 
7
- RSpec.shared_context "factory:stub", factory: :stub do
7
+ RSpec.shared_context "factory:stub" do
8
8
  prepend_before(:all) { TestProf::FactoryAllStub.enable! }
9
9
  append_after(:all) { TestProf::FactoryAllStub.disable! }
10
10
  end
11
+
12
+ RSpec.configure do |config|
13
+ config.include_context "factory:stub", factory: :stub
14
+ end