test-prof 0.12.2 → 1.0.0.rc1
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 +4 -4
- data/CHANGELOG.md +12 -0
- data/config/default.yml +0 -15
- data/config/rubocop-rspec.yml +6 -0
- data/lib/test_prof/any_fixture.rb +116 -7
- data/lib/test_prof/any_fixture/dump.rb +207 -0
- data/lib/test_prof/any_fixture/dump/base_adapter.rb +43 -0
- data/lib/test_prof/any_fixture/dump/digest.rb +29 -0
- data/lib/test_prof/any_fixture/dump/postgresql.rb +91 -0
- data/lib/test_prof/any_fixture/dump/sqlite.rb +42 -0
- data/lib/test_prof/before_all/adapters/active_record.rb +0 -5
- data/lib/test_prof/rubocop.rb +0 -1
- data/lib/test_prof/version.rb +1 -1
- metadata +10 -8
- data/lib/test_prof/cops/rspec/aggregate_failures.rb +0 -26
- data/lib/test_prof/ext/active_record_3.rb +0 -27
- data/lib/test_prof/recipes/active_record_one_love.rb +0 -6
- data/lib/test_prof/recipes/active_record_shared_connection.rb +0 -77
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b43e971bbd8c55a38c657e3338dc625d5860bd067e96385f310885024a1c5cc5
|
4
|
+
data.tar.gz: 6d4b767d0ed366be1b27521e986785b6ddfa9f2008eb9640c76baae55084bb34
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1b490801a31def008b6e8c77dff834e67c4c27a96d9d09fbe626dbed0009cb541da48acc7308905f3f8b722e8fc6f6bde39ebce5362e928abb0c2d319f259248
|
7
|
+
data.tar.gz: 825b2ca0cd5f0c4ea235c5df11686a39799a70685a1ae0911facceb383facec815bed04322336ec67867b51e3a0152d150ed9fe7db9f01bb1acea520fd095258
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,18 @@
|
|
2
2
|
|
3
3
|
## master (unrealeased)
|
4
4
|
|
5
|
+
## 1.0.0.rc1 (2020-12-30)
|
6
|
+
|
7
|
+
- Remove deprecated `AggregateFailures` cop. ([@palkan][])
|
8
|
+
|
9
|
+
- Remove `ActiveRecordSharedConnection`. ([@palkan][])
|
10
|
+
|
11
|
+
- Add `AnyFixture#register_dump` to _cache_ fixtures using SQL dumps. ([@palkan][])
|
12
|
+
|
13
|
+
- Replaced `TestProf::AnyFixture.reporting_enabled = true` with `TestProf::AnyFixture.config.reporting_enabled = true`. ([@palkan][])
|
14
|
+
|
15
|
+
- Add support for RSpec aliases detection when linting specs using `let_it_be`/`before_all` with `rubocop-rspec` 2.0 ([@pirj][])
|
16
|
+
|
5
17
|
## 0.12.2 (2020-09-03)
|
6
18
|
|
7
19
|
- Execute Minitest `before_all` in the context of the current test object. ([@palkan][])
|
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
|
@@ -1,6 +1,7 @@
|
|
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.
|
@@ -9,6 +10,58 @@ module TestProf
|
|
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 reporting_enabled? reporting_enabled
|
20
|
+
alias 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_given?
|
46
|
+
@before_dump << block
|
47
|
+
else
|
48
|
+
@before_dump
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def after_dump(&block)
|
53
|
+
if block_given?
|
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
|
-
|
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
|
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 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
|
-
|
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
|
-
|
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
|
@@ -0,0 +1,207 @@
|
|
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
|
+
@file = File.open(tmp_path, "w")
|
49
|
+
end
|
50
|
+
|
51
|
+
def start(_event, _id, payload)
|
52
|
+
sql = payload.fetch(:sql)
|
53
|
+
return if sql.match?(ANY_FIXTURE_IGNORE_RXP)
|
54
|
+
|
55
|
+
matches = sql.match(MODIFY_RXP)
|
56
|
+
return unless matches
|
57
|
+
|
58
|
+
reset_pk!(matches[2]) if /insert/i.match?(matches[1])
|
59
|
+
end
|
60
|
+
|
61
|
+
def finish(_event, _id, payload)
|
62
|
+
sql = payload.fetch(:sql)
|
63
|
+
return unless trackable_sql?(sql)
|
64
|
+
|
65
|
+
sql = payload[:binds].any? ? adapter.compile_sql(sql, quoted(payload[:binds])) : +sql
|
66
|
+
|
67
|
+
sql.tr!("\n", " ")
|
68
|
+
|
69
|
+
file.write(sql + ";\n")
|
70
|
+
end
|
71
|
+
|
72
|
+
def commit
|
73
|
+
file.close
|
74
|
+
|
75
|
+
FileUtils.mv(tmp_path, path)
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
attr_reader :file, :reset_pk, :adapter
|
81
|
+
|
82
|
+
def reset_pk!(table_name)
|
83
|
+
return if /sqlite_sequence/.match?(table_name)
|
84
|
+
|
85
|
+
return if reset_pk.include?(table_name)
|
86
|
+
|
87
|
+
adapter.reset_sequence!(table_name, AnyFixture.config.dump_sequence_random_start)
|
88
|
+
reset_pk << table_name
|
89
|
+
end
|
90
|
+
|
91
|
+
def trackable_sql?(sql)
|
92
|
+
return false if sql.match?(ANY_FIXTURE_IGNORE_RXP)
|
93
|
+
|
94
|
+
sql.match?(MODIFY_RXP) || sql.match?(ANY_FIXTURE_RXP) || sql.match?(AnyFixture.config.dump_matching_queries)
|
95
|
+
end
|
96
|
+
|
97
|
+
def quoted(val)
|
98
|
+
if val.is_a?(Array)
|
99
|
+
val.map { |v| quoted(v) }
|
100
|
+
elsif val.is_a?(ActiveRecord::Relation::QueryAttribute)
|
101
|
+
quoted(val.value_for_database)
|
102
|
+
else
|
103
|
+
ActiveRecord::Base.connection.quote(val)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
attr_reader :name, :digest, :path, :subscriber, :success
|
109
|
+
alias success? success
|
110
|
+
|
111
|
+
def initialize(name, watch: [], cache_key: nil)
|
112
|
+
@name = name
|
113
|
+
@digest = [
|
114
|
+
Digest.call(*watch),
|
115
|
+
cache_key.to_digest
|
116
|
+
].compact.join("-")
|
117
|
+
|
118
|
+
@path = build_path(name, digest)
|
119
|
+
|
120
|
+
@success = false
|
121
|
+
|
122
|
+
@adapter =
|
123
|
+
case ActiveRecord::Base.connection.adapter_name
|
124
|
+
when /sqlite/i
|
125
|
+
require "test_prof/any_fixture/dump/sqlite"
|
126
|
+
SQLite.new
|
127
|
+
when /postgresql/i
|
128
|
+
require "test_prof/any_fixture/dump/postgresql"
|
129
|
+
PostgreSQL.new
|
130
|
+
else
|
131
|
+
raise ArgumentError,
|
132
|
+
"Your current database adapter (#{ActiveRecord::Base.connection.adapter_name}) " \
|
133
|
+
"is currently not supported. So far, we only support SQLite and PostgreSQL"
|
134
|
+
end
|
135
|
+
|
136
|
+
@subscriber = Subscriber.new(path, adapter)
|
137
|
+
end
|
138
|
+
|
139
|
+
def exists?
|
140
|
+
File.exist?(path)
|
141
|
+
end
|
142
|
+
|
143
|
+
def force?
|
144
|
+
AnyFixture.config.force_matching_dumps.match?(name)
|
145
|
+
end
|
146
|
+
|
147
|
+
def load
|
148
|
+
return import_via_active_record unless AnyFixture.config.import_dump_via_cli?
|
149
|
+
|
150
|
+
adapter.import(path) || import_via_active_record
|
151
|
+
end
|
152
|
+
|
153
|
+
def commit!
|
154
|
+
subscriber.commit
|
155
|
+
end
|
156
|
+
|
157
|
+
def within_prepared_env(before: nil, after: nil, import: false)
|
158
|
+
run_before_callbacks(callback: before, dump: self, import: false)
|
159
|
+
yield.tap do
|
160
|
+
@success = true
|
161
|
+
end
|
162
|
+
ensure
|
163
|
+
run_after_callbacks(callback: after, dump: self, import: false)
|
164
|
+
end
|
165
|
+
|
166
|
+
private
|
167
|
+
|
168
|
+
attr_reader :adapter
|
169
|
+
|
170
|
+
def import_via_active_record
|
171
|
+
conn = ActiveRecord::Base.connection
|
172
|
+
|
173
|
+
File.open(path).each_line do |query|
|
174
|
+
next if query.empty?
|
175
|
+
|
176
|
+
conn.execute query
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def build_path(name, digest)
|
181
|
+
dir = TestProf.artifact_path(
|
182
|
+
File.join(AnyFixture.config.dumps_dir)
|
183
|
+
)
|
184
|
+
|
185
|
+
FileUtils.mkdir_p(dir)
|
186
|
+
|
187
|
+
File.join(dir, "#{name}-#{digest}.sql")
|
188
|
+
end
|
189
|
+
|
190
|
+
def run_before_callbacks(callback:, **options)
|
191
|
+
# First, call config-defined setup callbacks
|
192
|
+
AnyFixture.config.before_dump.each { |clbk| clbk.call(**options) }
|
193
|
+
# Then, adapter-defined callbacks
|
194
|
+
adapter.setup_env unless options[:import]
|
195
|
+
# Finally, user-provided callback
|
196
|
+
callback&.call(**options)
|
197
|
+
end
|
198
|
+
|
199
|
+
def run_after_callbacks(callback:, **options)
|
200
|
+
# The order is vice versa to setup
|
201
|
+
callback&.call(**options)
|
202
|
+
adapter.teardown_env unless options[:import]
|
203
|
+
AnyFixture.config.after_dump.each { |clbk| clbk.call(**options) }
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
@@ -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
|
data/lib/test_prof/rubocop.rb
CHANGED
data/lib/test_prof/version.rb
CHANGED
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.
|
4
|
+
version: 1.0.0.rc1
|
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-
|
11
|
+
date: 2020-12-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -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
|
@@ -228,9 +230,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
228
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:
|
235
|
+
version: 1.3.1
|
234
236
|
requirements: []
|
235
237
|
rubygems_version: 3.0.6
|
236
238
|
signing_key:
|
@@ -1,26 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module RSpec
|
6
|
-
class AggregateExamples
|
7
|
-
def self.registry
|
8
|
-
RuboCop::Cop::Cop.registry
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
class AggregateFailures < AggregateExamples
|
13
|
-
def initialize(*)
|
14
|
-
super
|
15
|
-
self.class.just_once { warn "`AggregateFailures` cop has been renamed to `AggregateExamples`." }
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.just_once
|
19
|
-
return if @already_done
|
20
|
-
yield
|
21
|
-
@already_done = true
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module TestProf
|
4
|
-
# Add missing `begin_transaction` and `rollback_transaction` methods
|
5
|
-
module ActiveRecord3Transactions
|
6
|
-
refine ::ActiveRecord::ConnectionAdapters::AbstractAdapter do
|
7
|
-
def begin_transaction(joinable: true)
|
8
|
-
if open_transactions > 0
|
9
|
-
increment_open_transactions
|
10
|
-
create_savepoint
|
11
|
-
else
|
12
|
-
begin_db_transaction
|
13
|
-
end
|
14
|
-
self.transaction_joinable = joinable
|
15
|
-
end
|
16
|
-
|
17
|
-
def rollback_transaction(*)
|
18
|
-
if open_transactions > 1
|
19
|
-
rollback_to_savepoint
|
20
|
-
else
|
21
|
-
rollback_db_transaction
|
22
|
-
end
|
23
|
-
decrement_open_transactions
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
@@ -1,77 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module TestProf
|
4
|
-
# Forces ActiveRecord to use the same connection between threads
|
5
|
-
module ActiveRecordSharedConnection # :nodoc: all
|
6
|
-
class << self
|
7
|
-
attr_reader :connection
|
8
|
-
|
9
|
-
def enable!
|
10
|
-
self.connection = ActiveRecord::Base.connection
|
11
|
-
end
|
12
|
-
|
13
|
-
def disable!
|
14
|
-
self.connection = nil
|
15
|
-
end
|
16
|
-
|
17
|
-
def ignore(&block)
|
18
|
-
raise ArgumentError, "Block is required" unless block_given?
|
19
|
-
|
20
|
-
@ignores ||= []
|
21
|
-
|
22
|
-
ignores << block
|
23
|
-
end
|
24
|
-
|
25
|
-
def ignored?(config)
|
26
|
-
!ignores.nil? && ignores.any? { |clbk| clbk.call(config) }
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
attr_reader :ignores
|
32
|
-
|
33
|
-
def connection=(conn)
|
34
|
-
@connection = conn
|
35
|
-
connection.singleton_class.prepend Connection
|
36
|
-
connection
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
module Connection
|
41
|
-
def shared_lock
|
42
|
-
@shared_lock ||= Mutex.new
|
43
|
-
end
|
44
|
-
|
45
|
-
def exec_cache(*)
|
46
|
-
shared_lock.synchronize { super }
|
47
|
-
end
|
48
|
-
|
49
|
-
def exec_no_cache(*)
|
50
|
-
shared_lock.synchronize { super }
|
51
|
-
end
|
52
|
-
|
53
|
-
def execute(*)
|
54
|
-
shared_lock.synchronize { super }
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
module Ext
|
59
|
-
def connection
|
60
|
-
return super if ActiveRecordSharedConnection.ignored?(connection_config)
|
61
|
-
ActiveRecordSharedConnection.connection || super
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
ActiveSupport.on_load(:active_record) do
|
68
|
-
if ::ActiveRecord::Base.connection.pool.respond_to?(:lock_thread=)
|
69
|
-
TestProf.log :warn, "You activated ActiveRecordSharedConnection patch for the Rails version,\n" \
|
70
|
-
"which has a built-in support for the same functionality.\n" \
|
71
|
-
"Consider removing it, 'cause this could result in unexpected behaviour.\n\n" \
|
72
|
-
"Read more in the docs: https://test-prof.evilmartians.io/#/active_record_shared_connection"
|
73
|
-
end
|
74
|
-
|
75
|
-
TestProf::ActiveRecordSharedConnection.enable!
|
76
|
-
ActiveRecord::Base.singleton_class.prepend TestProf::ActiveRecordSharedConnection::Ext
|
77
|
-
end
|