test-prof 1.0.5 → 1.0.8

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: edd58f04fbf02f8bfa863b2c68e19f26fbdfb95c36bb361d012848f0a4f76790
4
- data.tar.gz: 820c00eeaaea782bc89581005ac83cc147551550477519d7dd1a59a212f497c9
3
+ metadata.gz: 9e1623dde5386ea71184b81cf99eb67ac377613d46b2ebac3ce874164103fcbd
4
+ data.tar.gz: 1b9fa94cb2616226019cbc57ee8dbefa5c03db8f42f0be277352884ab6dfdb48
5
5
  SHA512:
6
- metadata.gz: f999f2073374210ef7c2afbedc977c21210f69e6e180fb54f6ce452741db1841b396b9358c9892e85ae972019d7204676d8a906d6a9f1f896c3669249027c935
7
- data.tar.gz: 38c1f0d45d386acca3edbbd7c52b9d246f02f030cce0717f2480b2917b003c6f2b3542beb6caac45d8fe432bc69b6a7db1720768297eb4a1588f471727664409
6
+ metadata.gz: 7eeec86920c82c3afb8b89478b3992bf1750fdacd949913c0a6c18a684bcefb5998880e4da2cecec1c8d5866dcc89b8a16de31e7e881713f8a4707aa08b25fd6
7
+ data.tar.gz: 793ec788e3cb95c29b8b3b1a9026db05e64d1ae828ca2ce8a180ffc460cf3d653710b2c4356e47d0dbc3aa8a3fd5ea633502fa5c0f0114a467a5dd01ae99b5b5
data/CHANGELOG.md CHANGED
@@ -1,6 +1,28 @@
1
1
  # Change log
2
2
 
3
- ## master (unrealeased)
3
+ ## master (unreleased)
4
+
5
+ ## 1.0.8 (2022-03-11)
6
+
7
+ - Restore the lock_thread value after rollback. ([@cou929][])
8
+
9
+ - Fixes the configuration of a printer for factory_prof runs
10
+
11
+ - Ensure that defaults are stored in a threadsafe manner
12
+
13
+ ## 1.0.7 (2021-08-30)
14
+
15
+ - Fix access to `let_it_be` variables in `after(:all)` hook. ([@cbarton][])
16
+
17
+ - Add support for using the before_all hook with Rails' parallelize feature (using processes). ([@peret][])
18
+
19
+ Make sure to include `TestProf::BeforeAll::Minitest` before you call `parallelize`.
20
+
21
+ ## 1.0.6 (2021-06-23)
22
+
23
+ - Fix Spring detection when `DISABLE_SPRING=1` is used. ([@palkan][])
24
+
25
+ - Make `before_all` in Minitest inheritable. ([@palkan][])
4
26
 
5
27
  ## 1.0.5 (2021-05-13)
6
28
 
@@ -178,7 +200,7 @@ end
178
200
  ```
179
201
 
180
202
  - Print warning when `ActiveRecordSharedConnection` is used in the version of Rails
181
- supporting `lock_threads` (5.1+). ([@palkan][])
203
+ supporting `lock_threads` (5.1+). ([@palkan][])
182
204
 
183
205
  ## 0.9.0 (2019-05-14)
184
206
 
@@ -236,20 +258,22 @@ See [changelog](https://github.com/test-prof/test-prof/blob/v0.8.0/CHANGELOG.md)
236
258
  [@palkan]: https://github.com/palkan
237
259
  [@marshall-lee]: https://github.com/marshall-lee
238
260
  [@danielwestendorf]: https://github.com/danielwestendorf
239
- [@Shkrt]: https://github.com/Shkrt
240
- [@IDolgirev]: https://github.com/IDolgirev
261
+ [@shkrt]: https://github.com/Shkrt
262
+ [@idolgirev]: https://github.com/IDolgirev
241
263
  [@desoleary]: https://github.com/desoleary
242
264
  [@rabotyaga]: https://github.com/rabotyaga
243
- [@Vasfed]: https://github.com/Vasfed
265
+ [@vasfed]: https://github.com/Vasfed
244
266
  [@szemek]: https://github.com/szemek
245
267
  [@mkldon]: https://github.com/mkldon
246
268
  [@dmagro]: https://github.com/dmagro
247
269
  [@danielwaterworth]: https://github.com/danielwaterworth
248
- [@Envek]: https://github.com/Envek
270
+ [@envek]: https://github.com/Envek
249
271
  [@tyleriguchi]: https://github.com/tyleriguchi
250
272
  [@lostie]: https://github.com/lostie
251
273
  [@pirj]: https://github.com/pirj
252
- [@LynxEyes]: https://github.com/LynxEyes
274
+ [@lynxeyes]: https://github.com/LynxEyes
253
275
  [@stefkin]: https://github.com/stefkin
254
276
  [@jaimerson]: https://github.com/jaimerson
255
277
  [@alexvko]: https://github.com/alexvko
278
+ [@grillermo]: https://github.com/grillermo
279
+ [@cou929]: https://github.com/cou929
@@ -23,7 +23,7 @@ module TestProf
23
23
  end
24
24
 
25
25
  def compile_sql(sql, binds)
26
- sql.gsub(/\$\d+/) { binds.shift }
26
+ sql.gsub(/\$\d+/) { binds.shift.gsub("\n", "' || chr(10) || '") }
27
27
  end
28
28
 
29
29
  def import(path)
@@ -18,7 +18,7 @@ module TestProf
18
18
  end
19
19
 
20
20
  def compile_sql(sql, binds)
21
- sql.gsub(/\?/) { binds.shift }
21
+ sql.gsub(/\?/) { binds.shift.gsub("\n", "' || char(10) || '") }
22
22
  end
23
23
 
24
24
  def import(path)
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "test_prof"
3
4
  require "test_prof/ext/float_duration"
4
5
  require "test_prof/any_fixture/dump"
5
6
 
@@ -37,6 +37,9 @@ module TestProf
37
37
  end
38
38
  end
39
39
 
40
+ # avoid instance variable collisions with cats
41
+ PREFIX_RESTORE_LOCK_THREAD = "@😺"
42
+
40
43
  configure do |config|
41
44
  # Make sure ActiveRecord uses locked thread.
42
45
  # It only gets locked in `before` / `setup` hook,
@@ -44,8 +47,14 @@ module TestProf
44
47
  # might lead to leaking connections
45
48
  config.before(:begin) do
46
49
  next unless ::ActiveRecord::Base.connection.pool.respond_to?(:lock_thread=)
50
+ instance_variable_set("#{PREFIX_RESTORE_LOCK_THREAD}_orig_lock_thread", ::ActiveRecord::Base.connection.pool.instance_variable_get(:@lock_thread))
47
51
  ::ActiveRecord::Base.connection.pool.lock_thread = true
48
52
  end
53
+
54
+ config.after(:rollback) do
55
+ next unless ::ActiveRecord::Base.connection.pool.respond_to?(:lock_thread=)
56
+ ::ActiveRecord::Base.connection.pool.lock_thread = instance_variable_get("#{PREFIX_RESTORE_LOCK_THREAD}_orig_lock_thread")
57
+ end
49
58
  end
50
59
  end
51
60
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "test_prof"
4
+
3
5
  module TestProf
4
6
  # `before_all` helper configuration
5
7
  module BeforeAll
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../language"
3
+ require "test_prof/cops/rspec/language"
4
4
 
5
5
  module RuboCop
6
6
  module Cop
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../language"
3
+ require "test_prof/cops/rspec/language"
4
4
 
5
5
  module RuboCop
6
6
  module Cop
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "aggregate_examples/line_range_helpers"
4
- require_relative "aggregate_examples/metadata_helpers"
5
- require_relative "aggregate_examples/node_matchers"
3
+ require "test_prof/cops/rspec/aggregate_examples/line_range_helpers"
4
+ require "test_prof/cops/rspec/aggregate_examples/metadata_helpers"
5
+ require "test_prof/cops/rspec/aggregate_examples/node_matchers"
6
6
 
7
- require_relative "aggregate_examples/its"
8
- require_relative "aggregate_examples/matchers_with_side_effects"
7
+ require "test_prof/cops/rspec/aggregate_examples/its"
8
+ require "test_prof/cops/rspec/aggregate_examples/matchers_with_side_effects"
9
9
 
10
10
  module RuboCop
11
11
  module Cop
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "test_prof"
3
4
  require "test_prof/factory_bot"
4
5
  require "test_prof/factory_all_stub/factory_bot_patch"
5
6
 
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "test_prof"
3
4
  require "test_prof/factory_bot"
4
5
  require "test_prof/factory_default/factory_bot_patch"
5
6
 
@@ -31,7 +32,6 @@ module TestProf
31
32
  TestProf::FactoryBot::Strategy::Build.prepend StrategyExt
32
33
  TestProf::FactoryBot::Strategy::Stub.prepend StrategyExt
33
34
 
34
- @store = {}
35
35
  # default is false to retain backward compatibility
36
36
  @preserve_traits = false
37
37
  end
@@ -57,12 +57,14 @@ module TestProf
57
57
  end
58
58
 
59
59
  def reset
60
- @store.clear
60
+ store.clear
61
61
  end
62
62
 
63
63
  private
64
64
 
65
- attr_reader :store
65
+ def store
66
+ Thread.current[:testprof_factory_store] ||= {}
67
+ end
66
68
  end
67
69
  end
68
70
  end
@@ -100,15 +100,21 @@ module TestProf
100
100
  def run
101
101
  init
102
102
 
103
- printer = config.printer
104
-
105
103
  started_at = TestProf.now
106
104
 
107
- at_exit { printer.dump(result, start_time: started_at) }
105
+ at_exit do
106
+ print(started_at)
107
+ end
108
108
 
109
109
  start
110
110
  end
111
111
 
112
+ def print(started_at)
113
+ printer = config.printer
114
+
115
+ printer.dump(result, start_time: started_at)
116
+ end
117
+
112
118
  def start
113
119
  reset!
114
120
  @running = true
@@ -24,8 +24,6 @@ module TestProf
24
24
  ]
25
25
  end
26
26
 
27
- # rubocop:disable Metrics/CyclomaticComplexity
28
- # rubocop:disable Metrics/PerceivedComplexity
29
27
  def all_loggables
30
28
  return @all_loggables if instance_variable_defined?(:@all_loggables)
31
29
 
@@ -2,6 +2,22 @@
2
2
 
3
3
  require "test_prof/before_all"
4
4
 
5
+ Minitest.singleton_class.prepend(Module.new do
6
+ attr_reader :previous_klass
7
+ @previous_klass = nil
8
+
9
+ def run_one_method(klass, method_name)
10
+ return super unless klass.respond_to?(:parallelized) && klass.parallelized
11
+
12
+ if @previous_klass && @previous_klass != klass
13
+ @previous_klass.before_all_executor&.deactivate!
14
+ end
15
+ @previous_klass = klass
16
+
17
+ super
18
+ end
19
+ end)
20
+
5
21
  module TestProf
6
22
  module BeforeAll
7
23
  # Add before_all hook to Minitest: wrap all examples into a transaction and
@@ -9,13 +25,15 @@ module TestProf
9
25
  module Minitest # :nodoc: all
10
26
  class Executor
11
27
  attr_reader :active, :block, :captured_ivars, :teardown_block, :current_test_object,
12
- :setup_fixtures
28
+ :setup_fixtures, :parent
13
29
 
14
30
  alias_method :active?, :active
15
31
  alias_method :setup_fixtures?, :setup_fixtures
16
32
 
17
- def initialize(setup_fixtures: false, &block)
18
- @setup_fixtures = setup_fixtures
33
+ def initialize(setup_fixtures: false, parent: nil, &block)
34
+ @parent = parent
35
+ # Fixtures must be instantiated if any of the executors needs them
36
+ @setup_fixtures = setup_fixtures || parent&.setup_fixtures
19
37
  @block = block
20
38
  @captured_ivars = []
21
39
  end
@@ -28,7 +46,9 @@ module TestProf
28
46
  @current_test_object = test_object
29
47
 
30
48
  return restore_ivars(test_object) if active?
49
+
31
50
  @active = true
51
+
32
52
  BeforeAll.setup_fixtures(test_object) if setup_fixtures?
33
53
  BeforeAll.begin_transaction do
34
54
  capture!(test_object)
@@ -36,20 +56,20 @@ module TestProf
36
56
  end
37
57
 
38
58
  def deactivate!
59
+ return unless active
60
+
39
61
  @active = false
40
62
 
41
- current_test_object&.instance_eval(&teardown_block) if teardown_block
63
+ perform_teardown(current_test_object)
42
64
 
43
65
  @current_test_object = nil
44
66
  BeforeAll.rollback_transaction
45
67
  end
46
68
 
47
69
  def capture!(test_object)
48
- return unless block
49
-
50
70
  before_ivars = test_object.instance_variables
51
71
 
52
- test_object.instance_eval(&block)
72
+ perform_setup(test_object)
53
73
 
54
74
  (test_object.instance_variables - before_ivars).each do |ivar|
55
75
  captured_ivars << [ivar, test_object.instance_variable_get(ivar)]
@@ -64,19 +84,76 @@ module TestProf
64
84
  )
65
85
  end
66
86
  end
87
+
88
+ def perform_setup(test_object)
89
+ parent&.perform_setup(test_object)
90
+ test_object.instance_eval(&block) if block
91
+ end
92
+
93
+ def perform_teardown(test_object)
94
+ current_test_object&.instance_eval(&teardown_block) if teardown_block
95
+ parent&.perform_teardown(test_object)
96
+ end
67
97
  end
68
98
 
69
99
  class << self
70
100
  def included(base)
71
101
  base.extend ClassMethods
102
+
103
+ base.cattr_accessor :parallelized
104
+ if base.respond_to?(:parallelize_teardown)
105
+ base.parallelize_teardown do
106
+ last_klass = ::Minitest.previous_klass
107
+ if last_klass&.respond_to?(:parallelized) && last_klass&.parallelized
108
+ last_klass.before_all_executor&.deactivate!
109
+ end
110
+ end
111
+ end
112
+
113
+ if base.respond_to?(:parallelize)
114
+ base.singleton_class.prepend(Module.new do
115
+ def parallelize(workers: :number_of_processors, with: :processes)
116
+ # super.parallelize returns nil when no parallelization is set up
117
+ if super(workers: workers, with: with).nil?
118
+ return
119
+ end
120
+
121
+ case with
122
+ when :processes
123
+ self.parallelized = true
124
+ when :threads
125
+ warn "!!! before_all is not implemented for parallalization with threads and " \
126
+ "could work incorrectly"
127
+ else
128
+ warn "!!! tests are using an unknown parallelization strategy and before_all " \
129
+ "could work incorrectly"
130
+ end
131
+ end
132
+ end)
133
+ end
72
134
  end
73
135
  end
74
136
 
75
137
  module ClassMethods
76
- attr_accessor :before_all_executor
138
+ attr_writer :before_all_executor
139
+
140
+ def before_all_executor
141
+ return @before_all_executor if instance_variable_defined?(:@before_all_executor)
142
+
143
+ @before_all_executor = if superclass.respond_to?(:before_all_executor)
144
+ superclass.before_all_executor
145
+ end
146
+ end
77
147
 
78
148
  def before_all(setup_fixtures: BeforeAll.config.setup_fixtures, &block)
79
- self.before_all_executor = Executor.new(setup_fixtures: setup_fixtures, &block)
149
+ self.before_all_executor = Executor.new(
150
+ setup_fixtures: setup_fixtures,
151
+ parent: before_all_executor,
152
+ &block
153
+ )
154
+
155
+ # Do not add patches multiple times
156
+ return if before_all_executor.parent
80
157
 
81
158
  prepend(Module.new do
82
159
  def before_setup
@@ -89,13 +166,13 @@ module TestProf
89
166
  def run(*)
90
167
  super
91
168
  ensure
92
- before_all_executor&.deactivate!
169
+ before_all_executor&.deactivate! unless parallelized
93
170
  end
94
171
  end)
95
172
  end
96
173
 
97
174
  def after_all(&block)
98
- self.before_all_executor ||= Executor.new
175
+ self.before_all_executor = Executor.new(parent: before_all_executor)
99
176
  before_all_executor.teardown(&block)
100
177
  end
101
178
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "test_prof"
4
- require_relative "./before_all"
4
+ require "test_prof/recipes/rspec/before_all"
5
5
 
6
6
  module TestProf
7
7
  # Just like `let`, but persist the result for the whole group.
@@ -75,7 +75,7 @@ module TestProf
75
75
  # Use uniq prefix for instance variables to avoid collisions
76
76
  # We want to use the power of Ruby's unicode support)
77
77
  # And we love cats!)
78
- PREFIX = RUBY_ENGINE == "jruby" ? "@__jruby_is_not_cat_friendly__" : "@😸"
78
+ PREFIX = "@😸"
79
79
 
80
80
  FROZEN_ERROR_HINT = "\nIf you are using `let_it_be`, you may want to pass `reload: true` or `refind: true` modifier to it."
81
81
 
@@ -107,7 +107,7 @@ module TestProf
107
107
  define_method(identifier) do
108
108
  # Trying to detect the context
109
109
  # Based on https://github.com/rspec/rspec-rails/commit/7cb796db064f58da7790a92e73ab906ef50b1f34
110
- if @__inspect_output.include?("before(:context)") || @__inspect_output.include?("before_all")
110
+ if /(before|after)\(:context\)/.match?(@__inspect_output) || @__inspect_output.include?("before_all")
111
111
  instance_variable_get(:"#{PREFIX}#{identifier}")
112
112
  else
113
113
  # Fallback to let definition
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "./base"
3
+ require "test_prof/rspec_dissect/collectors/base"
4
4
 
5
5
  module TestProf
6
6
  module RSpecDissect
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "./base"
3
+ require "test_prof/rspec_dissect/collectors/base"
4
4
 
5
5
  module TestProf
6
6
  module RSpecDissect
@@ -28,8 +28,6 @@ module TestProf
28
28
  end
29
29
 
30
30
  class << self
31
- # rubocop: disable Metrics/CyclomaticComplexity
32
- # rubocop: disable Metrics/PerceivedComplexity
33
31
  def parse(code)
34
32
  sexp = Ripper.sexp(code)
35
33
  return unless sexp
@@ -116,8 +116,6 @@ module TestProf
116
116
 
117
117
  private
118
118
 
119
- # rubocop: disable Metrics/CyclomaticComplexity
120
- # rubocop: disable Metrics/PerceivedComplexity
121
119
  def stamp_example(example, tags)
122
120
  matches = example.match(EXAMPLE_RXP)
123
121
  return false unless matches
@@ -9,5 +9,5 @@ end
9
9
 
10
10
  require "rubocop"
11
11
 
12
- require_relative "cops/inject"
12
+ require "test_prof/cops/inject"
13
13
  require "test_prof/cops/rspec/aggregate_examples"
@@ -162,13 +162,13 @@ module TestProf
162
162
  log :info, <<~MSG
163
163
  Run the following command to generate a flame graph report:
164
164
 
165
- stackprof --flamegraph #{path} > #{html_path} && stackprof --flamegraph-viewer=#{html_path}
165
+ stackprof --d3-flamegraph #{path} > #{html_path} && stackprof --flamegraph-viewer=#{html_path}
166
166
  MSG
167
167
  end
168
168
 
169
169
  def dump_json_report(path)
170
170
  report = ::StackProf::Report.new(
171
- Marshal.load(IO.binread(path)) # rubocop:disable Security/MarshalLoad
171
+ Marshal.load(IO.binread(path))
172
172
  )
173
173
  json_path = path.gsub(/\.dump$/, ".json")
174
174
  File.write(json_path, JSON.generate(report.data))
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TestProf
4
- VERSION = "1.0.5"
4
+ VERSION = "1.0.8"
5
5
  end
data/lib/test_prof.rb CHANGED
@@ -46,6 +46,13 @@ module TestProf
46
46
  defined?(Minitest)
47
47
  end
48
48
 
49
+ # Returns true if Spring is used and not disabled
50
+ def spring?
51
+ # See https://github.com/rails/spring/blob/577cf01f232bb6dbd0ade7df2df2ac209697e741/lib/spring/binstub.rb
52
+ disabled = ENV["DISABLE_SPRING"]
53
+ defined?(::Spring::Application) && (disabled.nil? || disabled.empty? || disabled == "0")
54
+ end
55
+
49
56
  # Returns the current process time
50
57
  def now
51
58
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
@@ -65,7 +72,7 @@ module TestProf
65
72
  # equal to the provided value (if any).
66
73
  # Contains workaround for applications using Spring.
67
74
  def activate(env_var, val = nil)
68
- if defined?(::Spring::Application)
75
+ if spring?
69
76
  notify_spring_detected
70
77
  ::Spring.after_fork do
71
78
  activate!(env_var, val) do
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.0.5
4
+ version: 1.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-13 00:00:00.000000000 Z
11
+ date: 2022-04-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -237,7 +237,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
237
237
  - !ruby/object:Gem::Version
238
238
  version: '0'
239
239
  requirements: []
240
- rubygems_version: 3.2.15
240
+ rubygems_version: 3.3.7
241
241
  signing_key:
242
242
  specification_version: 4
243
243
  summary: Ruby applications tests profiling tools