test-prof 0.7.5 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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