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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d382abde5291f54f1168e9403e3151aaafbe6e6e7409de394169169a0eca19fe
4
- data.tar.gz: 68037c8c7d819c6e68d7e8d4a4291471101539a6239f452e630de5ea0a4648d6
3
+ metadata.gz: 65bd3447c63c3e7c037707ebd0f637831ccf0c98af1b0b3d9bf0a6fa72c56b11
4
+ data.tar.gz: d6af1b43e6924c67ef4c2297602ef641377584f3282eafc236e48eb951e04b68
5
5
  SHA512:
6
- metadata.gz: fe73709e9e6d6e609f4a0f705d2de0dbab37daecffeb1bac91a4b1a7585a9f81bc656489e9b84d43ad5a9dd04be93a5b9b1020eecd370cbbfee2d2c2fbc862ce
7
- data.tar.gz: 91ff9f2c02b86260c9d0d88b834775528007b05eefa3fb00ba28ce9464dbfc615ae7ddde33f2d7544bfc048602687da078b6d01bd38038609b1ca33bb83500fe
6
+ metadata.gz: 2eae07fcbce70eb7598b92fc578530ed5e20edfb3d7752c672740d44aa13f5d925005500aa26e83ce004c0b2ba128b0091362c39771b67e12d352360aacc2081
7
+ data.tar.gz: 213858acbdaa8d5e2e6e1fcdd99ca952a044949d23846f631a34513d379ce583013c13ed76c0303c8bd8fec777f44b99bf992e8937598d22bf4110cedb408eb6
@@ -2,6 +2,24 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.8.0 (2019-04-12) 🚀
6
+
7
+ - **Ruby 2.4+ is requiered** ([@palkan][])
8
+
9
+ - **RSpec 3.5+ is requiered for RSpec features** ([@palkan][])
10
+
11
+ - Make `before_all` compatible with [`isolator`](https://github.com/palkan/isolator). ([@palkan][])
12
+
13
+ - Add `with_logging` and `with_ar_logging` helpers to logging recipe. ([@palkan][])
14
+
15
+ - Make `before_all` for Active Record `lock_thread` aware. ([@palkan][])
16
+
17
+ `before_all` can went crazy if you open multiple connections within it
18
+ (since it tracks the number of open transactions).
19
+ Rails 5+ `lock_thread` feature only locks the connection thread in
20
+ `before`/`setup` hook thus making it possible to have multiple connections/transactions
21
+ in `before_all` (e.g. performing jobs with Active Job async adapter).
22
+
5
23
  ## 0.7.5 (2019-02-22)
6
24
 
7
25
  - Make `let_it_be` and `before_all` work with `include_context`. ([@palkan][])
@@ -58,7 +76,7 @@ Add ability to specify custom exclusions through `config.custom_exclusions`, e.g
58
76
 
59
77
  ```ruby
60
78
  TestProf::RubyProf.configure do |config|
61
- config.custom_exclusions = { User => %i[save save!] }
79
+ config.custom_exclusions = {User => %i[save save!]}
62
80
  end
63
81
  ```
64
82
 
@@ -161,7 +179,7 @@ LOG=all rspec
161
179
  Or per example (group):
162
180
 
163
181
  ```ruby
164
- it 'does smth weird', :log do
182
+ it "does smth weird", :log do
165
183
  # ...
166
184
  end
167
185
  ```
data/README.md CHANGED
@@ -33,9 +33,11 @@ Of course, we have some [solutions](https://test-prof.evilmartians.io/#/?id=reci
33
33
 
34
34
  Supported Ruby versions:
35
35
 
36
- - Ruby (MRI) >= 2.3.0 (**NOTE:** for Ruby 2.2 use TestProf < 0.7.0)
36
+ - Ruby (MRI) >= 2.4.0 (**NOTE:** for Ruby 2.2 use TestProf < 0.7.0 or Ruby 2.3 use TestProf ~> 0.7.0)
37
37
 
38
- - JRuby >= 9.1.0.0
38
+ - JRuby >= 9.1.0.0 (**NOTE:** refinements-dependent features might require 9.2.7+)
39
+
40
+ Supported RSpec version (for RSpec features only): >= 3.5.0 (for older RSpec versions use TestProf < 0.8.0).
39
41
 
40
42
  <a href="https://evilmartians.com/">
41
43
  <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
@@ -62,7 +64,7 @@ Add `test-prof` gem to your application:
62
64
 
63
65
  ```ruby
64
66
  group :test do
65
- gem 'test-prof'
67
+ gem "test-prof"
66
68
  end
67
69
  ```
68
70
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'minitest'
4
- require 'test_prof/logging'
3
+ require "minitest"
4
+ require "test_prof/logging"
5
5
 
6
6
  module Minitest
7
7
  module TestProf
@@ -15,17 +15,23 @@ module Minitest
15
15
  inject_to_minitest_reporters if defined? Minitest::Reporters
16
16
  end
17
17
 
18
- def start; end
18
+ def start
19
+ end
19
20
 
20
- def prerecord(group, example); end
21
+ def prerecord(group, example)
22
+ end
21
23
 
22
- def before_test(test); end
24
+ def before_test(test)
25
+ end
23
26
 
24
- def record(*); end
27
+ def record(*)
28
+ end
25
29
 
26
- def after_test(test); end
30
+ def after_test(test)
31
+ end
27
32
 
28
- def report; end
33
+ def report
34
+ end
29
35
 
30
36
  private
31
37
 
@@ -33,22 +39,22 @@ module Minitest
33
39
  # Minitest::Result (>= 5.11) has `source_location` method
34
40
  return group.source_location if group.respond_to?(:source_location)
35
41
  if group.is_a? Class
36
- suite = group.public_instance_methods.select { |mtd| mtd.to_s.match /^test_/ }
42
+ suite = group.public_instance_methods.select { |mtd| mtd.to_s.match(/^test_/) }
37
43
  name = suite.find { |mtd| mtd.to_s == example }
38
44
  group.instance_method(name).source_location
39
45
  else
40
- suite = group.methods.select { |mtd| mtd.to_s.match /^test_/ }
46
+ suite = group.methods.select { |mtd| mtd.to_s.match(/^test_/) }
41
47
  name = suite.find { |mtd| mtd.to_s == group.name }
42
48
  group.method(name).source_location
43
49
  end
44
50
  end
45
51
 
46
52
  def location_with_line_number(group, example = nil)
47
- File.expand_path(location(group, example).join(':')).gsub(Dir.getwd, '.')
53
+ File.expand_path(location(group, example).join(":")).gsub(Dir.getwd, ".")
48
54
  end
49
55
 
50
56
  def location_without_line_number(group, example = nil)
51
- File.expand_path(location(group, example).first).gsub(Dir.getwd, '.')
57
+ File.expand_path(location(group, example).first).gsub(Dir.getwd, ".")
52
58
  end
53
59
 
54
60
  def inject_to_minitest_reporters
@@ -1,17 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'test_prof/event_prof/minitest'
4
- require 'test_prof/factory_doctor/minitest'
3
+ require "test_prof/event_prof/minitest"
4
+ require "test_prof/factory_doctor/minitest"
5
5
 
6
6
  module Minitest # :nodoc:
7
7
  module TestProf # :nodoc:
8
8
  def self.configure_options(options = {})
9
9
  options.tap do |opts|
10
- opts[:event] = ENV['EVENT_PROF'] if ENV['EVENT_PROF']
11
- opts[:rank_by] = ENV['EVENT_PROF_RANK'].to_sym if ENV['EVENT_PROF_RANK']
12
- opts[:top_count] = ENV['EVENT_PROF_TOP'].to_i if ENV['EVENT_PROF_TOP']
13
- opts[:per_example] = true if ENV['EVENT_PROF_EXAMPLES']
14
- opts[:fdoc] = true if ENV['FDOC']
10
+ opts[:event] = ENV["EVENT_PROF"] if ENV["EVENT_PROF"]
11
+ opts[:rank_by] = ENV["EVENT_PROF_RANK"].to_sym if ENV["EVENT_PROF_RANK"]
12
+ opts[:top_count] = ENV["EVENT_PROF_TOP"].to_i if ENV["EVENT_PROF_TOP"]
13
+ opts[:per_example] = true if ENV["EVENT_PROF_EXAMPLES"]
14
+ opts[:fdoc] = true if ENV["FDOC"]
15
15
  end
16
16
  end
17
17
  end
@@ -29,7 +29,7 @@ module Minitest # :nodoc:
29
29
  opts.on "--event-prof-per-example", TrueClass, "Includes examples metrics to results" do |flag|
30
30
  options[:per_example] = flag
31
31
  end
32
- opts.on "--factory-doctor", TrueClass, 'Enable Factory Doctor for your examples' do |flag|
32
+ opts.on "--factory-doctor", TrueClass, "Enable Factory Doctor for your examples" do |flag|
33
33
  options[:fdoc] = flag
34
34
  end
35
35
  end
@@ -114,13 +114,13 @@ module TestProf
114
114
  def with_timestamps(path)
115
115
  return path unless config.timestamps?
116
116
  timestamps = "-#{now.to_i}"
117
- "#{path.sub(/\.\w+$/, '')}#{timestamps}#{::File.extname(path)}"
117
+ "#{path.sub(/\.\w+$/, "")}#{timestamps}#{::File.extname(path)}"
118
118
  end
119
119
 
120
120
  def with_report_suffix(path)
121
121
  return path if config.report_suffix.nil?
122
122
 
123
- "#{path.sub(/\.\w+$/, '')}-#{config.report_suffix}#{::File.extname(path)}"
123
+ "#{path.sub(/\.\w+$/, "")}-#{config.report_suffix}#{::File.extname(path)}"
124
124
  end
125
125
 
126
126
  def notify_spring_detected
@@ -147,7 +147,7 @@ module TestProf
147
147
  @color = true
148
148
  @output_dir = "tmp/test_prof"
149
149
  @timestamps = false
150
- @report_suffix = ENV['TEST_PROF_REPORT']
150
+ @report_suffix = ENV["TEST_PROF_REPORT"]
151
151
  end
152
152
 
153
153
  def color?
@@ -27,7 +27,7 @@ module TestProf
27
27
 
28
28
  ts = TestProf.now
29
29
  store[key] = yield
30
- stats[key] = { time: TestProf.now - ts, hit: 0 }
30
+ stats[key] = {time: TestProf.now - ts, hit: 0}
31
31
  store[key]
32
32
  end
33
33
 
@@ -91,7 +91,7 @@ module TestProf
91
91
 
92
92
  msgs << format(
93
93
  "%#{first_column}s %12s %9s %12s",
94
- 'key', 'build time', 'hit count', 'saved time'
94
+ "key", "build time", "hit count", "saved time"
95
95
  )
96
96
 
97
97
  msgs << ""
@@ -144,6 +144,6 @@ module TestProf
144
144
  end
145
145
  end
146
146
 
147
- self.reporting_enabled = ENV['ANYFIXTURE_REPORT'] == '1'
147
+ self.reporting_enabled = ENV["ANYFIXTURE_REPORT"] == "1"
148
148
  end
149
149
  end
@@ -18,6 +18,11 @@ module TestProf
18
18
  def begin_transaction
19
19
  raise AdapterMissing if adapter.nil?
20
20
  adapter.begin_transaction
21
+ yield
22
+ end
23
+
24
+ def within_transaction
25
+ yield
21
26
  end
22
27
 
23
28
  def rollback_transaction
@@ -33,3 +38,7 @@ if defined?(::ActiveRecord::Base)
33
38
 
34
39
  TestProf::BeforeAll.adapter = TestProf::BeforeAll::Adapters::ActiveRecord
35
40
  end
41
+
42
+ if defined?(::Isolator)
43
+ require "test_prof/before_all/isolator"
44
+ end
@@ -12,6 +12,7 @@ module TestProf
12
12
  module ActiveRecord
13
13
  class << self
14
14
  def begin_transaction
15
+ lock_thread!
15
16
  ::ActiveRecord::Base.connection.begin_transaction(joinable: false)
16
17
  end
17
18
 
@@ -23,6 +24,17 @@ module TestProf
23
24
  end
24
25
  ::ActiveRecord::Base.connection.rollback_transaction
25
26
  end
27
+
28
+ private
29
+
30
+ # Make sure ActiveRecord uses locked thread.
31
+ # It only gets locked in `before` / `setup` hook,
32
+ # thus using thread in `before_all` (e.g. ActiveJob async adapter)
33
+ # might lead to leaking connections
34
+ def lock_thread!
35
+ return unless ::ActiveRecord::Base.connection.pool.respond_to?(:lock_thread=)
36
+ ::ActiveRecord::Base.connection.pool.lock_thread = true
37
+ end
26
38
  end
27
39
  end
28
40
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TestProf
4
+ module BeforeAll
5
+ # Disable Isolator within before_all blocks
6
+ module Isolator
7
+ def begin_transaction(*)
8
+ ::Isolator.disable { super }
9
+ end
10
+
11
+ def within_transaction(*)
12
+ ::Isolator.disable { super }
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ TestProf::BeforeAll.singleton_class.prepend(TestProf::BeforeAll::Isolator)
@@ -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
@@ -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
@@ -26,7 +26,7 @@ module TestProf::EventProf::CustomEvents
26
26
  res =
27
27
  if @depth == 1
28
28
  ActiveSupport::Notifications.instrument(
29
- 'factory.create',
29
+ "factory.create",
30
30
  name: factory
31
31
  ) { yield }
32
32
  else
@@ -22,7 +22,7 @@ module TestProf::EventProf::CustomEvents
22
22
  res =
23
23
  if @depth == 1
24
24
  ActiveSupport::Notifications.instrument(
25
- 'sidekiq.inline'
25
+ "sidekiq.inline"
26
26
  ) { yield }
27
27
  else
28
28
  yield
@@ -38,7 +38,7 @@ end
38
38
 
39
39
  TestProf::EventProf::CustomEvents.register("sidekiq.inline") do
40
40
  if TestProf.require(
41
- 'sidekiq/testing',
41
+ "sidekiq/testing",
42
42
  <<~MSG
43
43
  Failed to load Sidekiq.
44
44
 
@@ -16,7 +16,7 @@ module TestProf::EventProf::CustomEvents
16
16
 
17
17
  def track
18
18
  ActiveSupport::Notifications.instrument(
19
- 'sidekiq.jobs'
19
+ "sidekiq.jobs"
20
20
  ) { yield }
21
21
  end
22
22
  end
@@ -25,7 +25,7 @@ end
25
25
 
26
26
  TestProf::EventProf::CustomEvents.register("sidekiq.jobs") do
27
27
  if TestProf.require(
28
- 'sidekiq/testing',
28
+ "sidekiq/testing",
29
29
  <<~MSG
30
30
  Failed to load Sidekiq.
31
31
 
@@ -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