test-prof 0.12.0 → 1.0.7

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +155 -463
  3. data/README.md +9 -9
  4. data/config/default.yml +0 -15
  5. data/config/rubocop-rspec.yml +6 -0
  6. data/lib/test_prof/any_fixture/dump/base_adapter.rb +43 -0
  7. data/lib/test_prof/any_fixture/dump/digest.rb +29 -0
  8. data/lib/test_prof/any_fixture/dump/postgresql.rb +91 -0
  9. data/lib/test_prof/any_fixture/dump/sqlite.rb +42 -0
  10. data/lib/test_prof/any_fixture/dump.rb +212 -0
  11. data/lib/test_prof/any_fixture.rb +117 -8
  12. data/lib/test_prof/before_all/adapters/active_record.rb +15 -5
  13. data/lib/test_prof/before_all.rb +11 -2
  14. data/lib/test_prof/cops/rspec/aggregate_examples/its.rb +1 -1
  15. data/lib/test_prof/cops/rspec/aggregate_examples/line_range_helpers.rb +1 -1
  16. data/lib/test_prof/cops/rspec/aggregate_examples/matchers_with_side_effects.rb +1 -1
  17. data/lib/test_prof/cops/rspec/aggregate_examples/metadata_helpers.rb +1 -1
  18. data/lib/test_prof/cops/rspec/aggregate_examples/node_matchers.rb +1 -1
  19. data/lib/test_prof/cops/rspec/aggregate_examples.rb +1 -1
  20. data/lib/test_prof/event_prof/custom_events.rb +1 -1
  21. data/lib/test_prof/event_prof/instrumentations/active_support.rb +1 -1
  22. data/lib/test_prof/event_prof/profiler.rb +3 -4
  23. data/lib/test_prof/factory_prof/nate_heckler.rb +16 -0
  24. data/lib/test_prof/factory_prof/printers/flamegraph.rb +1 -1
  25. data/lib/test_prof/factory_prof/printers/nate_heckler.rb +26 -0
  26. data/lib/test_prof/factory_prof/printers/simple.rb +6 -2
  27. data/lib/test_prof/factory_prof.rb +24 -4
  28. data/lib/test_prof/logging.rb +18 -12
  29. data/lib/test_prof/recipes/logging.rb +1 -1
  30. data/lib/test_prof/recipes/minitest/before_all.rb +126 -24
  31. data/lib/test_prof/recipes/rspec/any_fixture.rb +1 -1
  32. data/lib/test_prof/recipes/rspec/before_all.rb +11 -3
  33. data/lib/test_prof/recipes/rspec/let_it_be.rb +6 -8
  34. data/lib/test_prof/rspec_dissect.rb +2 -2
  35. data/lib/test_prof/rspec_stamp.rb +1 -1
  36. data/lib/test_prof/rubocop.rb +0 -1
  37. data/lib/test_prof/ruby_prof.rb +11 -9
  38. data/lib/test_prof/tag_prof/result.rb +1 -1
  39. data/lib/test_prof/tag_prof/rspec.rb +3 -5
  40. data/lib/test_prof/utils/sized_ordered_set.rb +2 -2
  41. data/lib/test_prof/version.rb +1 -1
  42. data/lib/test_prof.rb +17 -4
  43. metadata +19 -14
  44. data/lib/test_prof/cops/rspec/aggregate_failures.rb +0 -26
  45. data/lib/test_prof/ext/active_record_3.rb +0 -27
  46. data/lib/test_prof/recipes/active_record_one_love.rb +0 -6
  47. data/lib/test_prof/recipes/active_record_shared_connection.rb +0 -77
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  [![Cult Of Martians](http://cultofmartians.com/assets/badges/badge.svg)](https://cultofmartians.com)
2
- [![Gem Version](https://badge.fury.io/rb/test-prof.svg)](https://rubygems.org/gems/test-prof) [![Build](https://github.com/palkan/test-prof/workflows/Build/badge.svg)](https://github.com/palkan/test-prof/actions)
3
- [![JRuby Build](https://github.com/palkan/test-prof/workflows/JRuby%20Build/badge.svg)](https://github.com/palkan/test-prof/actions)
4
- [![Code Triagers Badge](https://www.codetriage.com/palkan/test-prof/badges/users.svg)](https://www.codetriage.com/palkan/test-prof)
2
+ [![Gem Version](https://badge.fury.io/rb/test-prof.svg)](https://rubygems.org/gems/test-prof) [![Build](https://github.com/test-prof/test-prof/workflows/Build/badge.svg)](https://github.com/test-prof/test-prof/actions)
3
+ [![JRuby Build](https://github.com/test-prof/test-prof/workflows/JRuby%20Build/badge.svg)](https://github.com/test-prof/test-prof/actions)
4
+ [![Code Triagers Badge](https://www.codetriage.com/test-prof/test-prof/badges/users.svg)](https://www.codetriage.com/test-prof/test-prof)
5
5
  [![Documentation](https://img.shields.io/badge/docs-link-brightgreen.svg)](https://test-prof.evilmartians.io)
6
6
 
7
7
  # Ruby Tests Profiling Toolbox
@@ -32,7 +32,7 @@ TestProf toolbox aims to help you identify bottlenecks in your test suite. It co
32
32
  📑 [Documentation](https://test-prof.evilmartians.io)
33
33
 
34
34
  <p align="center">
35
- <a href="http://bit.ly/test-prof-map">
35
+ <a href="http://bit.ly/test-prof-map-v1">
36
36
  <img src="./docs/assets/images/coggle.png" alt="TestProf map" width="738">
37
37
  </a>
38
38
  </p>
@@ -51,7 +51,7 @@ TestProf toolbox aims to help you identify bottlenecks in your test suite. It co
51
51
  - [CodeTriage](https://github.com/codetriage/codetriage)
52
52
  - [Dev.to](https://github.com/thepracticaldev/dev.to)
53
53
  - [Open Project](https://github.com/opf/openproject)
54
- - [...and others](https://github.com/palkan/test-prof/issues/73)
54
+ - [...and others](https://github.com/test-prof/test-prof/issues/73)
55
55
 
56
56
  ## Resources
57
57
 
@@ -75,7 +75,7 @@ Add `test-prof` gem to your application:
75
75
 
76
76
  ```ruby
77
77
  group :test do
78
- gem "test-prof"
78
+ gem "test-prof", "~> 1.0"
79
79
  end
80
80
  ```
81
81
 
@@ -83,7 +83,7 @@ And that's it)
83
83
 
84
84
  Supported Ruby versions:
85
85
 
86
- - Ruby (MRI) >= 2.4.0 (**NOTE:** for Ruby 2.2 use TestProf < 0.7.0 or Ruby 2.3 use TestProf ~> 0.7.0)
86
+ - Ruby (MRI) >= 2.5.0 (**NOTE:** for Ruby 2.2 use TestProf < 0.7.0, Ruby 2.3 use TestProf ~> 0.7.0, Ruby 2.4 use TestProf <0.12.0)
87
87
 
88
88
  - JRuby >= 9.1.0.0 (**NOTE:** refinements-dependent features might require 9.2.7+)
89
89
 
@@ -95,9 +95,9 @@ Check out our [docs][].
95
95
 
96
96
  ## What's next?
97
97
 
98
- Have an idea? [Propose](https://github.com/palkan/test-prof/issues/new) a feature request!
98
+ Have an idea? [Propose](https://github.com/test-prof/test-prof/issues/new) a feature request!
99
99
 
100
- Already using TestProf? [Share your story!](https://github.com/palkan/test-prof/issues/73)
100
+ Already using TestProf? [Share your story!](https://github.com/test-prof/test-prof/issues/73)
101
101
 
102
102
  ## License
103
103
 
data/config/default.yml CHANGED
@@ -18,18 +18,3 @@ RSpec/AggregateExamples:
18
18
  - validate_length_of
19
19
  - validate_inclusion_of
20
20
  - validates_exclusion_of
21
-
22
- # TODO: remove this one we hit 1.0
23
- RSpec/AggregateFailures:
24
- Description: Checks if example group contains two or more aggregatable examples.
25
- Enabled: false
26
- StyleGuide: https://rspec.rubystyle.guide/#expectation-per-example
27
- AddAggregateFailuresMetadata: true
28
- MatchersWithSideEffects:
29
- - allow_value
30
- - allow_values
31
- - validate_presence_of
32
- - validate_absence_of
33
- - validate_length_of
34
- - validate_inclusion_of
35
- - validates_exclusion_of
@@ -0,0 +1,6 @@
1
+ RSpec:
2
+ Language:
3
+ Helpers:
4
+ - let_it_be
5
+ Hooks:
6
+ - before_all
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TestProf
4
+ module AnyFixture
5
+ class Dump
6
+ class BaseAdapter
7
+ def reset_sequence!(_table_name, _start)
8
+ end
9
+
10
+ def compile_sql(sql, _binds)
11
+ sql
12
+ end
13
+
14
+ def setup_env
15
+ end
16
+
17
+ def teardown_env
18
+ end
19
+
20
+ def import(_path)
21
+ false
22
+ end
23
+
24
+ private
25
+
26
+ def while_disconnected
27
+ conn.disconnect!
28
+ yield
29
+ ensure
30
+ conn.reconnect!
31
+ end
32
+
33
+ def conn
34
+ ActiveRecord::Base.connection
35
+ end
36
+
37
+ def execute(query)
38
+ conn.execute(query)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest/sha1"
4
+
5
+ module TestProf
6
+ module AnyFixture
7
+ class Dump
8
+ module Digest
9
+ module_function
10
+
11
+ def call(*paths)
12
+ files = (AnyFixture.config.default_dump_watch_paths + paths).each_with_object([]) do |path_or_glob, acc|
13
+ if File.file?(path_or_glob)
14
+ acc << path_or_glob
15
+ else
16
+ acc = acc.concat Dir[path_or_glob]
17
+ end
18
+ acc
19
+ end
20
+
21
+ return if files.empty?
22
+
23
+ file_ids = files.sort.map { |f| "#{File.basename(f)}/#{::Digest::SHA1.file(f).hexdigest}" }
24
+ ::Digest::SHA1.hexdigest(file_ids.join("/"))
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_prof/any_fixture/dump/base_adapter"
4
+
5
+ module TestProf
6
+ module AnyFixture
7
+ class Dump
8
+ class PostgreSQL < BaseAdapter
9
+ UUID_FUNCTIONS = %w[
10
+ gen_random_uuid
11
+ uuid_generate_v4
12
+ ]
13
+
14
+ def reset_sequence!(table_name, start)
15
+ _pk, sequence = conn.pk_and_sequence_for(table_name)
16
+ return unless sequence
17
+
18
+ sequence_name = "#{sequence.schema}.#{sequence.identifier}"
19
+
20
+ execute <<~SQL
21
+ ALTER SEQUENCE #{sequence_name} RESTART WITH #{start}; -- any_fixture:dump
22
+ SQL
23
+ end
24
+
25
+ def compile_sql(sql, binds)
26
+ sql.gsub(/\$\d+/) { binds.shift }
27
+ end
28
+
29
+ def import(path)
30
+ # Test if psql is installed
31
+ `psql --version`
32
+
33
+ tasks = ActiveRecord::Tasks::PostgreSQLDatabaseTasks.new(conn.pool.spec.config.with_indifferent_access)
34
+
35
+ while_disconnected do
36
+ tasks.structure_load(path, "--output=/dev/null")
37
+ end
38
+
39
+ true
40
+ rescue Errno::ENOENT
41
+ false
42
+ end
43
+
44
+ def setup_env
45
+ # Mock UUID generating functions to provide consistent results
46
+ quoted_functions = UUID_FUNCTIONS.map { |func| "'#{func}'" }.join(", ")
47
+
48
+ @uuid_funcs = execute <<~SQL
49
+ SELECT
50
+ pp.proname, pn.nspname,
51
+ pg_get_functiondef(pp.oid) AS definition
52
+ FROM pg_proc pp
53
+ JOIN pg_namespace pn
54
+ ON pn.oid = pp.pronamespace
55
+ WHERE pp.proname in (#{quoted_functions})
56
+ ORDER BY pp.oid;
57
+ SQL
58
+
59
+ uuid_funcs.each do |(func, ns, _)|
60
+ execute <<~SQL
61
+ CREATE OR REPLACE FUNCTION #{ns}.#{func}()
62
+ RETURNS UUID
63
+ LANGUAGE SQL
64
+ AS $$
65
+ SELECT md5(random()::TEXT)::UUID;
66
+ $$; -- any_fixture:dump
67
+ SQL
68
+ end
69
+
70
+ execute <<~SQL
71
+ SELECT setseed(#{rand}); -- any_fixture:dump
72
+ SQL
73
+ end
74
+
75
+ def teardown_env
76
+ uuid_funcs.each do |(func, ns, definition)|
77
+ execute "#{definition}; -- any_fixture:dump"
78
+ end
79
+ end
80
+
81
+ private
82
+
83
+ attr_reader :uuid_funcs
84
+
85
+ def execute(query)
86
+ super.values
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_prof/any_fixture/dump/base_adapter"
4
+
5
+ module TestProf
6
+ module AnyFixture
7
+ class Dump
8
+ class SQLite < BaseAdapter
9
+ def reset_sequence!(table_name, start)
10
+ execute <<~SQL.chomp
11
+ DELETE FROM sqlite_sequence WHERE name=#{table_name}
12
+ SQL
13
+
14
+ execute <<~SQL.chomp
15
+ INSERT INTO sqlite_sequence (name, seq)
16
+ VALUES (#{table_name}, #{start})
17
+ SQL
18
+ end
19
+
20
+ def compile_sql(sql, binds)
21
+ sql.gsub(/\?/) { binds.shift }
22
+ end
23
+
24
+ def import(path)
25
+ db = conn.pool.spec.config[:database]
26
+ return false if %r{:memory:}.match?(db)
27
+
28
+ # Check that sqlite3 is installed
29
+ `sqlite3 --version`
30
+
31
+ while_disconnected do
32
+ `sqlite3 #{db} < "#{path}"`
33
+ end
34
+
35
+ true
36
+ rescue Errno::ENOENT
37
+ false
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,212 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_prof/any_fixture/dump/digest"
4
+
5
+ require "set"
6
+
7
+ module TestProf
8
+ module AnyFixture
9
+ MODIFY_RXP = /^(INSERT INTO|UPDATE|DELETE FROM) (\S+)/i.freeze
10
+ ANY_FIXTURE_RXP = /(\/\*|--).*\bany_fixture:dump/.freeze
11
+ ANY_FIXTURE_IGNORE_RXP = /(\/\*|--).*\bany_fixture:ignore/.freeze
12
+
13
+ using(Module.new do
14
+ refine Object do
15
+ def to_digest
16
+ to_s
17
+ end
18
+ end
19
+
20
+ refine NilClass do
21
+ def to_digest
22
+ nil
23
+ end
24
+ end
25
+
26
+ refine Hash do
27
+ def to_digest
28
+ map { |k, v| [k.to_digest, v.to_digest].compact.join("_") }
29
+ end
30
+ end
31
+
32
+ refine Array do
33
+ def to_digest
34
+ map { |v| v.to_digest }.compact.join("-")
35
+ end
36
+ end
37
+ end)
38
+
39
+ class Dump
40
+ class Subscriber
41
+ attr_reader :path, :tmp_path
42
+
43
+ def initialize(path, adapter)
44
+ @path = path
45
+ @adapter = adapter
46
+ @tmp_path = path + ".tmp"
47
+ @reset_pk = Set.new
48
+ end
49
+
50
+ def start(_event, _id, payload)
51
+ sql = payload.fetch(:sql)
52
+ return if sql.match?(ANY_FIXTURE_IGNORE_RXP)
53
+
54
+ matches = sql.match(MODIFY_RXP)
55
+ return unless matches
56
+
57
+ reset_pk!(matches[2]) if /insert/i.match?(matches[1])
58
+ end
59
+
60
+ def finish(_event, _id, payload)
61
+ sql = payload.fetch(:sql)
62
+ return unless trackable_sql?(sql)
63
+
64
+ sql = payload[:binds].any? ? adapter.compile_sql(sql, quoted(payload[:binds])) : +sql
65
+
66
+ sql.tr!("\n", " ")
67
+
68
+ file.write(sql + ";\n")
69
+ end
70
+
71
+ def commit
72
+ return unless defined?(:@file)
73
+
74
+ file.close
75
+
76
+ FileUtils.mv(tmp_path, path)
77
+ end
78
+
79
+ private
80
+
81
+ attr_reader :reset_pk, :adapter
82
+
83
+ def file
84
+ @file ||= File.open(tmp_path, "w")
85
+ end
86
+
87
+ def reset_pk!(table_name)
88
+ return if /sqlite_sequence/.match?(table_name)
89
+
90
+ return if reset_pk.include?(table_name)
91
+
92
+ adapter.reset_sequence!(table_name, AnyFixture.config.dump_sequence_random_start)
93
+ reset_pk << table_name
94
+ end
95
+
96
+ def trackable_sql?(sql)
97
+ return false if sql.match?(ANY_FIXTURE_IGNORE_RXP)
98
+
99
+ sql.match?(MODIFY_RXP) || sql.match?(ANY_FIXTURE_RXP) || sql.match?(AnyFixture.config.dump_matching_queries)
100
+ end
101
+
102
+ def quoted(val)
103
+ if val.is_a?(Array)
104
+ val.map { |v| quoted(v) }
105
+ elsif val.is_a?(ActiveModel::Attribute)
106
+ quoted(val.value_for_database)
107
+ else
108
+ ActiveRecord::Base.connection.quote(val)
109
+ end
110
+ end
111
+ end
112
+
113
+ attr_reader :name, :digest, :path, :subscriber, :success
114
+ alias_method :success?, :success
115
+
116
+ def initialize(name, watch: [], cache_key: nil)
117
+ @name = name
118
+ @digest = [
119
+ Digest.call(*watch),
120
+ cache_key.to_digest
121
+ ].compact.join("-")
122
+
123
+ @path = build_path(name, digest)
124
+
125
+ @success = false
126
+
127
+ @adapter =
128
+ case ActiveRecord::Base.connection.adapter_name
129
+ when /sqlite/i
130
+ require "test_prof/any_fixture/dump/sqlite"
131
+ SQLite.new
132
+ when /postgresql/i
133
+ require "test_prof/any_fixture/dump/postgresql"
134
+ PostgreSQL.new
135
+ else
136
+ raise ArgumentError,
137
+ "Your current database adapter (#{ActiveRecord::Base.connection.adapter_name}) " \
138
+ "is currently not supported. So far, we only support SQLite and PostgreSQL"
139
+ end
140
+
141
+ @subscriber = Subscriber.new(path, adapter)
142
+ end
143
+
144
+ def exists?
145
+ File.exist?(path)
146
+ end
147
+
148
+ def force?
149
+ AnyFixture.config.force_matching_dumps.match?(name)
150
+ end
151
+
152
+ def load
153
+ return import_via_active_record unless AnyFixture.config.import_dump_via_cli?
154
+
155
+ adapter.import(path) || import_via_active_record
156
+ end
157
+
158
+ def commit!
159
+ subscriber.commit
160
+ end
161
+
162
+ def within_prepared_env(before: nil, after: nil, import: false)
163
+ run_before_callbacks(callback: before, dump: self, import: false)
164
+ yield.tap do
165
+ @success = true
166
+ end
167
+ ensure
168
+ run_after_callbacks(callback: after, dump: self, import: false)
169
+ end
170
+
171
+ private
172
+
173
+ attr_reader :adapter
174
+
175
+ def import_via_active_record
176
+ conn = ActiveRecord::Base.connection
177
+
178
+ File.open(path).each_line do |query|
179
+ next if query.empty?
180
+
181
+ conn.execute query
182
+ end
183
+ end
184
+
185
+ def build_path(name, digest)
186
+ dir = TestProf.artifact_path(
187
+ File.join(AnyFixture.config.dumps_dir)
188
+ )
189
+
190
+ FileUtils.mkdir_p(dir)
191
+
192
+ File.join(dir, "#{name}-#{digest}.sql")
193
+ end
194
+
195
+ def run_before_callbacks(callback:, **options)
196
+ # First, call config-defined setup callbacks
197
+ AnyFixture.config.before_dump.each { |clbk| clbk.call(**options) }
198
+ # Then, adapter-defined callbacks
199
+ adapter.setup_env unless options[:import]
200
+ # Finally, user-provided callback
201
+ callback&.call(**options)
202
+ end
203
+
204
+ def run_after_callbacks(callback:, **options)
205
+ # The order is vice versa to setup
206
+ callback&.call(**options)
207
+ adapter.teardown_env unless options[:import]
208
+ AnyFixture.config.after_dump.each { |clbk| clbk.call(**options) }
209
+ end
210
+ end
211
+ end
212
+ end
@@ -1,14 +1,67 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "test_prof/ext/float_duration"
4
+ require "test_prof/any_fixture/dump"
4
5
 
5
6
  module TestProf
6
7
  # Make DB fixtures from blocks.
7
8
  module AnyFixture
8
- INSERT_RXP = /^INSERT INTO ([\S]+)/.freeze
9
+ INSERT_RXP = /^INSERT INTO (\S+)/.freeze
9
10
 
10
11
  using FloatDuration
11
12
 
13
+ # AnyFixture configuration
14
+ class Configuration
15
+ attr_accessor :reporting_enabled, :dumps_dir, :dump_sequence_start,
16
+ :import_dump_via_cli, :dump_matching_queries, :force_matching_dumps
17
+ attr_reader :default_dump_watch_paths
18
+
19
+ alias_method :reporting_enabled?, :reporting_enabled
20
+ alias_method :import_dump_via_cli?, :import_dump_via_cli
21
+
22
+ def initialize
23
+ @reporting_enabled = ENV["ANYFIXTURE_REPORT"] == "1"
24
+ @dumps_dir = "any_dumps"
25
+ @default_dump_watch_paths = %w[
26
+ db/schema.rb
27
+ db/structure.sql
28
+ ]
29
+ @dump_sequence_start = 123_654
30
+ @dump_matching_queries = /^$/
31
+ @import_dump_via_cli = ENV["ANYFIXTURE_IMPORT_DUMP_CLI"] == "1"
32
+ @before_dump = []
33
+ @after_dump = []
34
+ @force_matching_dumps =
35
+ if ENV["ANYFIXTURE_FORCE_DUMP"] == "1"
36
+ /.*/
37
+ elsif ENV["ANYFIXTURE_FORCE_DUMP"]
38
+ /#{ENV["ANYFIXTURE_FORCE_DUMP"]}/
39
+ else
40
+ /^$/
41
+ end
42
+ end
43
+
44
+ def before_dump(&block)
45
+ if block
46
+ @before_dump << block
47
+ else
48
+ @before_dump
49
+ end
50
+ end
51
+
52
+ def after_dump(&block)
53
+ if block
54
+ @after_dump << block
55
+ else
56
+ @after_dump
57
+ end
58
+ end
59
+
60
+ def dump_sequence_random_start
61
+ rand(dump_sequence_start..(dump_sequence_start * 2))
62
+ end
63
+ end
64
+
12
65
  class Cache # :nodoc:
13
66
  attr_reader :store, :stats
14
67
 
@@ -40,22 +93,74 @@ module TestProf
40
93
  class << self
41
94
  include Logging
42
95
 
43
- attr_accessor :reporting_enabled
96
+ def config
97
+ @config ||= Configuration.new
98
+ end
99
+
100
+ def configure
101
+ yield config
102
+ end
103
+
104
+ # Backward compatibility
105
+ def reporting_enabled=(val)
106
+ warn "AnyFixture.reporting_enabled is deprecated and will be removed in 1.1. Use AnyFixture.config.reporting_enabled instead"
107
+ config.reporting_enabled = val
108
+ end
44
109
 
45
- def reporting_enabled?
46
- reporting_enabled == true
110
+ def reporting_enabled
111
+ warn "AnyFixture.reporting_enabled is deprecated and will be removed in 1.1. Use AnyFixture.config.reporting_enabled instead"
112
+ config.reporting_enabled
47
113
  end
48
114
 
115
+ alias_method :reporting_enabled?, :reporting_enabled
116
+
49
117
  # Register a block of code as a fixture,
50
118
  # returns the result of the block execution
51
119
  def register(id)
52
- cache.fetch(id) do
120
+ cached(id) do
53
121
  ActiveSupport::Notifications.subscribed(method(:subscriber), "sql.active_record") do
54
122
  yield
55
123
  end
56
124
  end
57
125
  end
58
126
 
127
+ def cached(id)
128
+ cache.fetch(id) { yield }
129
+ end
130
+
131
+ # Create and register new SQL dump.
132
+ # Use `watch` to provide additional paths to watch for
133
+ # dump re-generation
134
+ def register_dump(name, clean: true, **options)
135
+ called_from = caller_locations(1, 1).first.path
136
+ watch = options.delete(:watch) || [called_from]
137
+ cache_key = options.delete(:cache_key)
138
+ skip = options.delete(:skip_if)
139
+
140
+ id = "sql/#{name}"
141
+
142
+ register_method = clean ? :register : :cached
143
+
144
+ public_send(register_method, id) do
145
+ dump = Dump.new(name, watch: watch, cache_key: cache_key)
146
+
147
+ unless dump.force?
148
+ next if skip&.call(dump: dump)
149
+
150
+ next dump.within_prepared_env(import: true, **options) { dump.load } if dump.exists?
151
+ end
152
+
153
+ subscriber = ActiveSupport::Notifications.subscribe("sql.active_record", dump.subscriber)
154
+ res = dump.within_prepared_env(**options) { yield }
155
+
156
+ dump.commit!
157
+
158
+ res
159
+ ensure
160
+ ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
161
+ end
162
+ end
163
+
59
164
  # Clean all affected tables (but do not reset cache)
60
165
  def clean
61
166
  disable_referential_integrity do
@@ -76,7 +181,13 @@ module TestProf
76
181
 
77
182
  def subscriber(_event, _start, _finish, _id, data)
78
183
  matches = data.fetch(:sql).match(INSERT_RXP)
79
- tables_cache[matches[1]] = true if matches
184
+ return unless matches
185
+
186
+ table_name = matches[1]
187
+
188
+ return if /sqlite_sequence/.match?(table_name)
189
+
190
+ tables_cache[table_name] = true
80
191
  end
81
192
 
82
193
  def report_stats
@@ -148,7 +259,5 @@ module TestProf
148
259
  connection.disable_referential_integrity { yield }
149
260
  end
150
261
  end
151
-
152
- self.reporting_enabled = ENV["ANYFIXTURE_REPORT"] == "1"
153
262
  end
154
263
  end