test-prof 0.11.3 → 1.0.0.rc2

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 (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +122 -447
  3. data/LICENSE.txt +1 -1
  4. data/README.md +9 -13
  5. data/config/default.yml +0 -15
  6. data/config/rubocop-rspec.yml +6 -0
  7. data/lib/minitest/test_prof_plugin.rb +3 -0
  8. data/lib/test_prof/any_fixture.rb +116 -7
  9. data/lib/test_prof/any_fixture/dump.rb +207 -0
  10. data/lib/test_prof/any_fixture/dump/base_adapter.rb +43 -0
  11. data/lib/test_prof/any_fixture/dump/digest.rb +29 -0
  12. data/lib/test_prof/any_fixture/dump/postgresql.rb +91 -0
  13. data/lib/test_prof/any_fixture/dump/sqlite.rb +42 -0
  14. data/lib/test_prof/before_all.rb +9 -4
  15. data/lib/test_prof/before_all/adapters/active_record.rb +14 -5
  16. data/lib/test_prof/cops/rspec/aggregate_examples.rb +2 -2
  17. data/lib/test_prof/cops/rspec/aggregate_examples/its.rb +1 -1
  18. data/lib/test_prof/cops/rspec/aggregate_examples/line_range_helpers.rb +1 -1
  19. data/lib/test_prof/cops/rspec/aggregate_examples/matchers_with_side_effects.rb +1 -1
  20. data/lib/test_prof/cops/rspec/aggregate_examples/metadata_helpers.rb +1 -1
  21. data/lib/test_prof/cops/rspec/aggregate_examples/node_matchers.rb +1 -1
  22. data/lib/test_prof/event_prof/instrumentations/active_support.rb +22 -4
  23. data/lib/test_prof/recipes/minitest/before_all.rb +48 -23
  24. data/lib/test_prof/recipes/minitest/sample.rb +6 -10
  25. data/lib/test_prof/recipes/rspec/before_all.rb +10 -10
  26. data/lib/test_prof/recipes/rspec/let_it_be.rb +111 -13
  27. data/lib/test_prof/recipes/rspec/sample.rb +4 -2
  28. data/lib/test_prof/rubocop.rb +0 -1
  29. data/lib/test_prof/stack_prof.rb +3 -0
  30. data/lib/test_prof/version.rb +1 -1
  31. metadata +23 -21
  32. data/lib/test_prof/cops/rspec/aggregate_failures.rb +0 -26
  33. data/lib/test_prof/ext/active_record_3.rb +0 -27
  34. data/lib/test_prof/recipes/active_record_one_love.rb +0 -6
  35. data/lib/test_prof/recipes/active_record_shared_connection.rb +0 -77
@@ -35,18 +35,14 @@ module TestProf
35
35
  end
36
36
  end
37
37
  end
38
- end
39
38
 
40
- # Overrides Minitest.run
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)
39
+ def call
40
+ if ENV["SAMPLE"]
41
+ ::TestProf::MinitestSample.sample_examples(ENV["SAMPLE"].to_i)
42
+ elsif ENV["SAMPLE_GROUPS"]
43
+ ::TestProf::MinitestSample.sample_groups(ENV["SAMPLE_GROUPS"].to_i)
44
+ end
46
45
  end
47
- super
48
46
  end
49
47
  end
50
48
  end
51
-
52
- Minitest.singleton_class.prepend(TestProf::MinitestSample)
@@ -6,14 +6,22 @@ module TestProf
6
6
  module BeforeAll
7
7
  # Helper to wrap the whole example group into a transaction
8
8
  module RSpec
9
- def before_all(&block)
9
+ def before_all(setup_fixtures: BeforeAll.config.setup_fixtures, &block)
10
10
  raise ArgumentError, "Block is required!" unless block_given?
11
11
 
12
- return within_before_all(&block) if within_before_all?
12
+ if within_before_all?
13
+ before(:all) do
14
+ @__inspect_output = "before_all hook"
15
+ instance_eval(&block)
16
+ end
17
+ return
18
+ end
13
19
 
14
20
  @__before_all_activated__ = true
15
21
 
16
22
  before(:all) do
23
+ @__inspect_output = "before_all hook"
24
+ BeforeAll.setup_fixtures(self) if setup_fixtures
17
25
  BeforeAll.begin_transaction do
18
26
  instance_eval(&block)
19
27
  end
@@ -24,14 +32,6 @@ module TestProf
24
32
  end
25
33
  end
26
34
 
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
-
35
35
  def within_before_all?
36
36
  instance_variable_defined?(:@__before_all_activated__)
37
37
  end
@@ -22,6 +22,10 @@ module TestProf
22
22
 
23
23
  LetItBe.modifiers[key] = block
24
24
  end
25
+
26
+ def default_modifiers
27
+ @default_modifiers ||= {}
28
+ end
25
29
  end
26
30
 
27
31
  class << self
@@ -75,6 +79,8 @@ module TestProf
75
79
  # And we love cats!)
76
80
  PREFIX = RUBY_ENGINE == "jruby" ? "@__jruby_is_not_cat_friendly__" : "@😸"
77
81
 
82
+ FROZEN_ERROR_HINT = "\nIf you are using `let_it_be`, you may want to pass `reload: true` or `refind: true` modifier to it."
83
+
78
84
  def self.define_let_it_be_alias(name, **default_args)
79
85
  define_method(name) do |identifier, **options, &blk|
80
86
  let_it_be(identifier, **default_args.merge(options), &blk)
@@ -83,27 +89,28 @@ module TestProf
83
89
 
84
90
  def let_it_be(identifier, **options, &block)
85
91
  initializer = proc do
86
- instance_variable_set(:"#{PREFIX}#{identifier}", instance_exec(&block))
92
+ instance_variable_set(:"#{TestProf::LetItBe::PREFIX}#{identifier}", instance_exec(&block))
93
+ rescue FrozenError => e
94
+ e.message << TestProf::LetItBe::FROZEN_ERROR_HINT
95
+ raise
87
96
  end
88
97
 
89
- if within_before_all?
90
- within_before_all(&initializer)
91
- else
92
- before_all(&initializer)
93
- end
98
+ default_options = LetItBe.config.default_modifiers.dup
99
+ default_options.merge!(metadata[:let_it_be_modifiers]) if metadata[:let_it_be_modifiers]
94
100
 
95
- define_let_it_be_methods(identifier, **options)
96
- end
101
+ options = default_options.merge(options)
102
+
103
+ before_all(&initializer)
97
104
 
98
- def define_let_it_be_methods(identifier, **modifiers)
99
- let_accessor = LetItBe.wrap_with_modifiers(modifiers) do
105
+ let_accessor = LetItBe.wrap_with_modifiers(options) do
100
106
  instance_variable_get(:"#{PREFIX}#{identifier}")
101
107
  end
102
108
 
103
109
  LetItBe.module_for(self).module_eval do
104
110
  define_method(identifier) do
105
- # Trying to detect the context (couldn't find other way so far)
106
- if /\(:context\)/.match?(@__inspect_output)
111
+ # Trying to detect the context
112
+ # Based on https://github.com/rspec/rspec-rails/commit/7cb796db064f58da7790a92e73ab906ef50b1f34
113
+ if @__inspect_output.include?("before(:context)") || @__inspect_output.include?("before_all")
107
114
  instance_variable_get(:"#{PREFIX}#{identifier}")
108
115
  else
109
116
  # Fallback to let definition
@@ -114,16 +121,78 @@ module TestProf
114
121
 
115
122
  let(identifier, &let_accessor)
116
123
  end
124
+
125
+ module Freezer
126
+ # Stoplist to prevent freezing objects and theirs associations that are defined
127
+ # with `let_it_be`'s `freeze: false` options during deep freezing.
128
+ #
129
+ # To only keep track of objects that are available in current example group,
130
+ # `begin` adds a new layer, and `rollback` removes a layer of unrelated objects
131
+ # along with rolling back the transaction where they were created.
132
+ #
133
+ # Stoplist holds records declared with `freeze: false` (so we do not freeze them even if they're used as
134
+ # associated records for frozen objects)
135
+ module Stoplist
136
+ class << self
137
+ def stop?(record)
138
+ @stoplist.any? { |layer| layer.include?(record) }
139
+ end
140
+
141
+ def stop!(record)
142
+ @stoplist.last.push(record)
143
+ end
144
+
145
+ def begin
146
+ @stoplist.push([])
147
+ end
148
+
149
+ def rollback
150
+ @stoplist.pop
151
+ end
152
+ end
153
+
154
+ # Stack of example group-related variable definitions
155
+ @stoplist = []
156
+ end
157
+
158
+ class << self
159
+ # Rerucsively freezes the object to detect modifications
160
+ def deep_freeze(record)
161
+ return if record.frozen?
162
+ return if Stoplist.stop?(record)
163
+
164
+ record.freeze
165
+
166
+ # Support `let_it_be` with `create_list`
167
+ return record.each { |rec| deep_freeze(rec) } if record.respond_to?(:each)
168
+
169
+ # Freeze associations as well.
170
+ return unless defined?(::ActiveRecord::Base)
171
+ return unless record.is_a?(::ActiveRecord::Base)
172
+
173
+ record.class.reflections.keys.each do |reflection|
174
+ # But only if they are already loaded. If not yet loaded, they weren't
175
+ # created by factories, and it's ok to mutate them.
176
+
177
+ next unless record.association(reflection.to_sym).loaded?
178
+
179
+ target = record.association(reflection.to_sym).target
180
+ deep_freeze(target) if target.is_a?(::ActiveRecord::Base) || target.respond_to?(:each)
181
+ end
182
+ end
183
+ end
184
+ end
117
185
  end
118
186
  end
119
187
 
120
- if defined?(::ActiveRecord)
188
+ if defined?(::ActiveRecord::Base)
121
189
  require "test_prof/ext/active_record_refind"
122
190
  using TestProf::Ext::ActiveRecordRefind
123
191
 
124
192
  TestProf::LetItBe.configure do |config|
125
193
  config.register_modifier :reload do |record, val|
126
194
  next record unless val
195
+
127
196
  next record.reload if record.is_a?(::ActiveRecord::Base)
128
197
 
129
198
  if record.respond_to?(:map)
@@ -136,6 +205,7 @@ if defined?(::ActiveRecord)
136
205
 
137
206
  config.register_modifier :refind do |record, val|
138
207
  next record unless val
208
+
139
209
  next record.refind if record.is_a?(::ActiveRecord::Base)
140
210
 
141
211
  if record.respond_to?(:map)
@@ -145,7 +215,35 @@ if defined?(::ActiveRecord)
145
215
  end
146
216
  record
147
217
  end
218
+
219
+ config.register_modifier :freeze do |record, val|
220
+ if val == false
221
+ TestProf::LetItBe::Freezer::Stoplist.stop!(record)
222
+ next record
223
+ end
224
+
225
+ TestProf::LetItBe::Freezer.deep_freeze(record)
226
+ record
227
+ end
148
228
  end
149
229
  end
150
230
 
151
231
  RSpec::Core::ExampleGroup.extend TestProf::LetItBe
232
+
233
+ TestProf::BeforeAll.configure do |config|
234
+ config.before(:begin) do
235
+ TestProf::LetItBe::Freezer::Stoplist.begin
236
+ end
237
+
238
+ config.after(:rollback) do
239
+ TestProf::LetItBe::Freezer::Stoplist.rollback
240
+ end
241
+ end
242
+
243
+ RSpec.configure do |config|
244
+ config.after(:example) do |example|
245
+ if example.exception&.is_a?(FrozenError)
246
+ example.exception.message << TestProf::LetItBe::FROZEN_ERROR_HINT
247
+ end
248
+ end
249
+ end
@@ -15,7 +15,8 @@ if ENV["SAMPLE"]
15
15
  RSpec.configure do |config|
16
16
  config.before(:suite) do
17
17
  filtered_examples = RSpec.world.filtered_examples.values.flatten
18
- sample = filtered_examples.sample(ENV["SAMPLE"].to_i)
18
+ random = Random.new(RSpec.configuration.seed)
19
+ sample = filtered_examples.sample(ENV["SAMPLE"].to_i, random: random)
19
20
  RSpec.world.filtered_examples = Hash.new do |hash, group|
20
21
  hash[group] = group.examples & sample
21
22
  end
@@ -31,7 +32,8 @@ if ENV["SAMPLE_GROUPS"]
31
32
  filtered_groups = RSpec.world.filtered_examples.reject do |_group, examples|
32
33
  examples.empty?
33
34
  end.keys
34
- sample = filtered_groups.sample(ENV["SAMPLE_GROUPS"].to_i)
35
+ random = Random.new(RSpec.configuration.seed)
36
+ sample = filtered_groups.sample(ENV["SAMPLE_GROUPS"].to_i, random: random)
35
37
  RSpec.world.filtered_examples = Hash.new do |hash, group|
36
38
  hash[group] = sample.include?(group) ? group.examples : []
37
39
  end
@@ -11,4 +11,3 @@ require "rubocop"
11
11
 
12
12
  require_relative "cops/inject"
13
13
  require "test_prof/cops/rspec/aggregate_examples"
14
- require "test_prof/cops/rspec/aggregate_failures"
@@ -36,6 +36,9 @@ module TestProf
36
36
  else
37
37
  "html"
38
38
  end
39
+
40
+ sample_interval = ENV["TEST_STACK_PROF_INTERVAL"].to_i
41
+ @interval = sample_interval > 0 ? sample_interval : nil
39
42
  end
40
43
 
41
44
  def raw?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TestProf
4
- VERSION = "0.11.3"
4
+ VERSION = "1.0.0.rc2"
5
5
  end
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: 0.11.3
4
+ version: 1.0.0.rc2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-11 00:00:00.000000000 Z
11
+ date: 2021-01-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '12.0'
33
+ version: '13.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '12.0'
40
+ version: '13.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -56,42 +56,42 @@ dependencies:
56
56
  name: isolator
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0.6'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0.6'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: minitest
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - "~>"
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
75
  version: '5.9'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - "~>"
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '5.9'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rubocop
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - "~>"
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  version: 0.77.0
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - "~>"
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: 0.77.0
97
97
  description: "\n Ruby applications tests profiling tools.\n\n Contains tools
@@ -119,6 +119,7 @@ files:
119
119
  - assets/tagprof.demo.html
120
120
  - assets/tagprof.template.html
121
121
  - config/default.yml
122
+ - config/rubocop-rspec.yml
122
123
  - lib/minitest/base_reporter.rb
123
124
  - lib/minitest/event_prof_formatter.rb
124
125
  - lib/minitest/test_prof_plugin.rb
@@ -126,6 +127,11 @@ files:
126
127
  - lib/test_prof.rb
127
128
  - lib/test_prof/any_fixture.rb
128
129
  - lib/test_prof/any_fixture/dsl.rb
130
+ - lib/test_prof/any_fixture/dump.rb
131
+ - lib/test_prof/any_fixture/dump/base_adapter.rb
132
+ - lib/test_prof/any_fixture/dump/digest.rb
133
+ - lib/test_prof/any_fixture/dump/postgresql.rb
134
+ - lib/test_prof/any_fixture/dump/sqlite.rb
129
135
  - lib/test_prof/before_all.rb
130
136
  - lib/test_prof/before_all/adapters/active_record.rb
131
137
  - lib/test_prof/before_all/isolator.rb
@@ -136,7 +142,6 @@ files:
136
142
  - lib/test_prof/cops/rspec/aggregate_examples/matchers_with_side_effects.rb
137
143
  - lib/test_prof/cops/rspec/aggregate_examples/metadata_helpers.rb
138
144
  - lib/test_prof/cops/rspec/aggregate_examples/node_matchers.rb
139
- - lib/test_prof/cops/rspec/aggregate_failures.rb
140
145
  - lib/test_prof/cops/rspec/language.rb
141
146
  - lib/test_prof/event_prof.rb
142
147
  - lib/test_prof/event_prof/custom_events.rb
@@ -148,7 +153,6 @@ files:
148
153
  - lib/test_prof/event_prof/monitor.rb
149
154
  - lib/test_prof/event_prof/profiler.rb
150
155
  - lib/test_prof/event_prof/rspec.rb
151
- - lib/test_prof/ext/active_record_3.rb
152
156
  - lib/test_prof/ext/active_record_refind.rb
153
157
  - lib/test_prof/ext/array_bsearch_index.rb
154
158
  - lib/test_prof/ext/factory_bot_strategy.rb
@@ -173,8 +177,6 @@ files:
173
177
  - lib/test_prof/factory_prof/printers/flamegraph.rb
174
178
  - lib/test_prof/factory_prof/printers/simple.rb
175
179
  - lib/test_prof/logging.rb
176
- - lib/test_prof/recipes/active_record_one_love.rb
177
- - lib/test_prof/recipes/active_record_shared_connection.rb
178
180
  - lib/test_prof/recipes/logging.rb
179
181
  - lib/test_prof/recipes/minitest/before_all.rb
180
182
  - lib/test_prof/recipes/minitest/sample.rb
@@ -208,15 +210,15 @@ files:
208
210
  - lib/test_prof/utils/rspec.rb
209
211
  - lib/test_prof/utils/sized_ordered_set.rb
210
212
  - lib/test_prof/version.rb
211
- homepage: http://github.com/palkan/test-prof
213
+ homepage: http://github.com/test-prof/test-prof
212
214
  licenses:
213
215
  - MIT
214
216
  metadata:
215
- bug_tracker_uri: http://github.com/palkan/test-prof/issues
216
- changelog_uri: https://github.com/palkan/test-prof/blob/master/CHANGELOG.md
217
+ bug_tracker_uri: http://github.com/test-prof/test-prof/issues
218
+ changelog_uri: https://github.com/test-prof/test-prof/blob/master/CHANGELOG.md
217
219
  documentation_uri: https://test-prof.evilmartians.io/
218
220
  homepage_uri: https://test-prof.evilmartians.io/
219
- source_code_uri: http://github.com/palkan/test-prof
221
+ source_code_uri: http://github.com/test-prof/test-prof
220
222
  post_install_message:
221
223
  rdoc_options: []
222
224
  require_paths:
@@ -225,12 +227,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
225
227
  requirements:
226
228
  - - ">="
227
229
  - !ruby/object:Gem::Version
228
- version: 2.4.0
230
+ version: 2.5.0
229
231
  required_rubygems_version: !ruby/object:Gem::Requirement
230
232
  requirements:
231
- - - ">="
233
+ - - ">"
232
234
  - !ruby/object:Gem::Version
233
- version: '0'
235
+ version: 1.3.1
234
236
  requirements: []
235
237
  rubygems_version: 3.0.6
236
238
  signing_key: