test-prof 1.0.5 → 1.0.8

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