test-prof 0.12.2 → 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|