test-prof 0.7.5 → 0.9.0
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 +174 -107
- data/LICENSE.txt +1 -1
- data/README.md +9 -3
- data/lib/minitest/base_reporter.rb +18 -12
- data/lib/minitest/test_prof_plugin.rb +8 -8
- data/lib/test_prof/any_fixture.rb +3 -3
- data/lib/test_prof/before_all/adapters/active_record.rb +11 -0
- data/lib/test_prof/before_all/isolator.rb +18 -0
- data/lib/test_prof/before_all.rb +82 -2
- data/lib/test_prof/cops/rspec/aggregate_failures.rb +6 -6
- data/lib/test_prof/event_prof/custom_events/factory_create.rb +27 -45
- data/lib/test_prof/event_prof/custom_events/sidekiq_inline.rb +8 -38
- data/lib/test_prof/event_prof/custom_events/sidekiq_jobs.rb +7 -25
- data/lib/test_prof/event_prof/instrumentations/active_support.rb +1 -1
- data/lib/test_prof/event_prof/minitest.rb +4 -4
- data/lib/test_prof/event_prof/monitor.rb +45 -8
- data/lib/test_prof/event_prof/rspec.rb +4 -11
- data/lib/test_prof/event_prof.rb +6 -6
- data/lib/test_prof/ext/factory_bot_strategy.rb +24 -0
- data/lib/test_prof/factory_bot.rb +1 -1
- data/lib/test_prof/factory_default.rb +1 -1
- data/lib/test_prof/factory_doctor/fabrication_patch.rb +12 -0
- data/lib/test_prof/factory_doctor/factory_bot_patch.rb +5 -1
- data/lib/test_prof/factory_doctor/minitest.rb +4 -4
- data/lib/test_prof/factory_doctor/rspec.rb +15 -12
- data/lib/test_prof/factory_doctor.rb +29 -7
- data/lib/test_prof/factory_prof/factory_builders/fabrication.rb +1 -1
- data/lib/test_prof/factory_prof/factory_builders/factory_bot.rb +2 -17
- data/lib/test_prof/factory_prof/printers/flamegraph.rb +6 -2
- data/lib/test_prof/factory_prof/printers/simple.rb +8 -6
- data/lib/test_prof/factory_prof.rb +29 -15
- data/lib/test_prof/recipes/logging.rb +86 -21
- data/lib/test_prof/recipes/minitest/before_all.rb +3 -2
- data/lib/test_prof/recipes/minitest/sample.rb +5 -5
- data/lib/test_prof/recipes/rspec/any_fixture.rb +3 -1
- data/lib/test_prof/recipes/rspec/before_all.rb +12 -3
- data/lib/test_prof/recipes/rspec/factory_all_stub.rb +5 -1
- data/lib/test_prof/recipes/rspec/let_it_be.rb +27 -12
- data/lib/test_prof/rspec_dissect/rspec.rb +2 -6
- data/lib/test_prof/rspec_dissect.rb +13 -19
- data/lib/test_prof/rspec_stamp/parser.rb +1 -1
- data/lib/test_prof/rspec_stamp/rspec.rb +1 -1
- data/lib/test_prof/rspec_stamp.rb +14 -14
- data/lib/test_prof/ruby_prof/rspec.rb +2 -6
- data/lib/test_prof/ruby_prof.rb +32 -24
- data/lib/test_prof/stack_prof/rspec.rb +5 -6
- data/lib/test_prof/stack_prof.rb +23 -15
- data/lib/test_prof/tag_prof/printers/simple.rb +4 -4
- data/lib/test_prof/tag_prof/result.rb +3 -3
- data/lib/test_prof/tag_prof/rspec.rb +9 -14
- data/lib/test_prof/utils/html_builder.rb +1 -1
- data/lib/test_prof/utils/sized_ordered_set.rb +1 -1
- data/lib/test_prof/version.rb +1 -1
- data/lib/test_prof.rb +6 -12
- metadata +35 -14
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "test_prof"
|
|
4
4
|
require "test_prof/factory_bot"
|
|
5
5
|
require "test_prof/factory_doctor/factory_bot_patch"
|
|
6
|
+
require "test_prof/factory_doctor/fabrication_patch"
|
|
6
7
|
|
|
7
8
|
module TestProf
|
|
8
9
|
# FactoryDoctor is a tool that helps you identify
|
|
@@ -18,7 +19,7 @@ module TestProf
|
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
def bad?
|
|
21
|
-
count > 0 && queries_count.zero?
|
|
22
|
+
count > 0 && queries_count.zero? && time >= FactoryDoctor.config.threshold
|
|
22
23
|
end
|
|
23
24
|
end
|
|
24
25
|
|
|
@@ -38,26 +39,47 @@ module TestProf
|
|
|
38
39
|
\ASAVEPOINT
|
|
39
40
|
)}xi.freeze
|
|
40
41
|
|
|
42
|
+
class Configuration
|
|
43
|
+
attr_accessor :event, :threshold
|
|
44
|
+
|
|
45
|
+
def initialize
|
|
46
|
+
# event to track for DB interactions
|
|
47
|
+
@event = ENV.fetch("FDOC_EVENT", "sql.active_record")
|
|
48
|
+
# consider result good if time wasted less then threshold
|
|
49
|
+
@threshold = ENV.fetch("FDOC_THRESHOLD", "0.01").to_f
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
41
53
|
class << self
|
|
42
54
|
include TestProf::Logging
|
|
43
55
|
|
|
44
|
-
attr_reader :event
|
|
45
56
|
attr_reader :count, :time, :queries_count
|
|
46
57
|
|
|
58
|
+
def config
|
|
59
|
+
@config ||= Configuration.new
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def configure
|
|
63
|
+
yield config
|
|
64
|
+
end
|
|
65
|
+
|
|
47
66
|
# Patch factory lib, init counters
|
|
48
|
-
def init
|
|
49
|
-
@event = event
|
|
67
|
+
def init
|
|
50
68
|
reset!
|
|
51
69
|
|
|
52
|
-
log :info, "FactoryDoctor enabled"
|
|
70
|
+
log :info, "FactoryDoctor enabled (event: \"#{config.event}\", threshold: #{config.threshold})"
|
|
53
71
|
|
|
54
72
|
# Monkey-patch FactoryBot / FactoryGirl
|
|
55
73
|
TestProf::FactoryBot::FactoryRunner.prepend(FactoryBotPatch) if
|
|
56
74
|
defined?(TestProf::FactoryBot)
|
|
57
75
|
|
|
76
|
+
# Monkey-patch Fabrication
|
|
77
|
+
::Fabricate.singleton_class.prepend(FabricationPatch) if
|
|
78
|
+
defined?(::Fabricate)
|
|
79
|
+
|
|
58
80
|
subscribe!
|
|
59
81
|
|
|
60
|
-
@stamp = ENV[
|
|
82
|
+
@stamp = ENV["FDOC_STAMP"]
|
|
61
83
|
|
|
62
84
|
RSpecStamp.config.tags = @stamp if stamp?
|
|
63
85
|
end
|
|
@@ -122,7 +144,7 @@ module TestProf
|
|
|
122
144
|
end
|
|
123
145
|
|
|
124
146
|
def subscribe!
|
|
125
|
-
::ActiveSupport::Notifications.subscribe(event) do |_name, _start, _finish, _id, query|
|
|
147
|
+
::ActiveSupport::Notifications.subscribe(config.event) do |_name, _start, _finish, _id, query|
|
|
126
148
|
next if ignore? || !running? || within_factory?
|
|
127
149
|
next if query[:sql] =~ IGNORED_QUERIES_PATTERN
|
|
128
150
|
@queries_count += 1
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "test_prof/factory_prof/factory_bot_patch"
|
|
4
4
|
require "test_prof/factory_bot"
|
|
5
|
+
require "test_prof/ext/factory_bot_strategy"
|
|
5
6
|
|
|
6
7
|
module TestProf
|
|
7
8
|
module FactoryProf
|
|
@@ -9,23 +10,7 @@ module TestProf
|
|
|
9
10
|
# implementation of #patch and #track methods
|
|
10
11
|
# to provide unified interface for all factory-building gems
|
|
11
12
|
class FactoryBot
|
|
12
|
-
|
|
13
|
-
# older versions and top-level invocations use Symbols
|
|
14
|
-
using(Module.new do
|
|
15
|
-
refine Symbol do
|
|
16
|
-
def create?
|
|
17
|
-
self == :create
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
if defined?(::FactoryBot::Strategy::Create)
|
|
22
|
-
refine Class do
|
|
23
|
-
def create?
|
|
24
|
-
self <= ::FactoryBot::Strategy::Create
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end)
|
|
13
|
+
using TestProf::FactoryBotStrategy
|
|
29
14
|
|
|
30
15
|
# Monkey-patch FactoryBot / FactoryGirl
|
|
31
16
|
def self.patch
|
|
@@ -15,7 +15,7 @@ module TestProf::FactoryProf
|
|
|
15
15
|
return log(:info, "No factories detected") if result.raw_stats == {}
|
|
16
16
|
report_data = {
|
|
17
17
|
total_stacks: result.stacks.size,
|
|
18
|
-
total: result.
|
|
18
|
+
total: result.total_count
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
report_data[:roots] = convert_stacks(result)
|
|
@@ -45,7 +45,11 @@ module TestProf::FactoryProf
|
|
|
45
45
|
node = paths[path]
|
|
46
46
|
node[:value] += 1
|
|
47
47
|
else
|
|
48
|
-
node = {
|
|
48
|
+
node = {
|
|
49
|
+
name: sample,
|
|
50
|
+
value: 1,
|
|
51
|
+
total: result.raw_stats.fetch(sample)[:total_count]
|
|
52
|
+
}
|
|
49
53
|
paths[path] = node
|
|
50
54
|
|
|
51
55
|
if parent.nil?
|
|
@@ -10,23 +10,25 @@ module TestProf::FactoryProf
|
|
|
10
10
|
return log(:info, "No factories detected") if result.raw_stats == {}
|
|
11
11
|
msgs = []
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
total_count = result.stats.sum { |stat| stat[:total_count] }
|
|
14
|
+
total_top_level_count = result.stats.sum { |stat| stat[:top_level_count] }
|
|
15
|
+
total_time = result.stats.sum { |stat| stat[:top_level_time] }
|
|
15
16
|
total_uniq_factories = result.stats.map { |stat| stat[:name] }.uniq.count
|
|
16
17
|
|
|
17
18
|
msgs <<
|
|
18
19
|
<<~MSG
|
|
19
20
|
Factories usage
|
|
20
21
|
|
|
21
|
-
Total: #{
|
|
22
|
-
Total top-level: #{
|
|
22
|
+
Total: #{total_count}
|
|
23
|
+
Total top-level: #{total_top_level_count}
|
|
24
|
+
Total time: #{format("%.4f", total_time)}s
|
|
23
25
|
Total uniq factories: #{total_uniq_factories}
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
total top-level total time top-level time name
|
|
26
28
|
MSG
|
|
27
29
|
|
|
28
30
|
result.stats.each do |stat|
|
|
29
|
-
msgs << format("%
|
|
31
|
+
msgs << format("%8d %11d %11.4fs %15.4fs %30s", stat[:total_count], stat[:top_level_count], stat[:total_time], stat[:top_level_time], stat[:name])
|
|
30
32
|
end
|
|
31
33
|
|
|
32
34
|
log :info, msgs.join("\n")
|
|
@@ -17,7 +17,7 @@ module TestProf
|
|
|
17
17
|
attr_accessor :mode
|
|
18
18
|
|
|
19
19
|
def initialize
|
|
20
|
-
@mode = ENV[
|
|
20
|
+
@mode = ENV["FPROF"] == "flamegraph" ? :flamegraph : :simple
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
# Whether we want to generate flamegraphs
|
|
@@ -36,23 +36,24 @@ module TestProf
|
|
|
36
36
|
|
|
37
37
|
# Returns sorted stats
|
|
38
38
|
def stats
|
|
39
|
-
|
|
39
|
+
@stats ||= @raw_stats.values
|
|
40
|
+
.sort_by { |el| -el[:total_count] }
|
|
41
|
+
end
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
def total_count
|
|
44
|
+
@total_count ||= @raw_stats.values.sum { |v| v[:total_count] }
|
|
43
45
|
end
|
|
44
46
|
|
|
45
|
-
def
|
|
46
|
-
|
|
47
|
-
@total = @raw_stats.values.sum { |v| v[:total] }
|
|
47
|
+
def total_time
|
|
48
|
+
@total_time ||= @raw_stats.values.sum { |v| v[:total_time] }
|
|
48
49
|
end
|
|
49
50
|
|
|
50
51
|
private
|
|
51
52
|
|
|
52
53
|
def sorted_stats(key)
|
|
53
54
|
@raw_stats.values
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
.map { |el| [el[:name], el[key]] }
|
|
56
|
+
.sort_by { |el| -el[1] }
|
|
56
57
|
end
|
|
57
58
|
end
|
|
58
59
|
|
|
@@ -103,13 +104,18 @@ module TestProf
|
|
|
103
104
|
|
|
104
105
|
def track(factory)
|
|
105
106
|
return yield unless running?
|
|
107
|
+
@depth += 1
|
|
108
|
+
@current_stack << factory if config.flamegraph?
|
|
109
|
+
@stats[factory][:total_count] += 1
|
|
110
|
+
@stats[factory][:top_level_count] += 1 if @depth == 1
|
|
111
|
+
t1 = TestProf.now
|
|
106
112
|
begin
|
|
107
|
-
@depth += 1
|
|
108
|
-
@current_stack << factory if config.flamegraph?
|
|
109
|
-
@stats[factory][:total] += 1
|
|
110
|
-
@stats[factory][:top_level] += 1 if @depth == 1
|
|
111
113
|
yield
|
|
112
114
|
ensure
|
|
115
|
+
t2 = TestProf.now
|
|
116
|
+
elapsed = t2 - t1
|
|
117
|
+
@stats[factory][:total_time] += elapsed
|
|
118
|
+
@stats[factory][:top_level_time] += elapsed if @depth == 1
|
|
113
119
|
@depth -= 1
|
|
114
120
|
flush_stack if @depth.zero?
|
|
115
121
|
end
|
|
@@ -120,7 +126,15 @@ module TestProf
|
|
|
120
126
|
def reset!
|
|
121
127
|
@stacks = [] if config.flamegraph?
|
|
122
128
|
@depth = 0
|
|
123
|
-
@stats = Hash.new
|
|
129
|
+
@stats = Hash.new do |h, k|
|
|
130
|
+
h[k] = {
|
|
131
|
+
name: k,
|
|
132
|
+
total_count: 0,
|
|
133
|
+
top_level_count: 0,
|
|
134
|
+
total_time: 0.0,
|
|
135
|
+
top_level_time: 0.0
|
|
136
|
+
}
|
|
137
|
+
end
|
|
124
138
|
flush_stack
|
|
125
139
|
end
|
|
126
140
|
|
|
@@ -137,6 +151,6 @@ module TestProf
|
|
|
137
151
|
end
|
|
138
152
|
end
|
|
139
153
|
|
|
140
|
-
TestProf.activate(
|
|
154
|
+
TestProf.activate("FPROF") do
|
|
141
155
|
TestProf::FactoryProf.run
|
|
142
156
|
end
|
|
@@ -2,44 +2,109 @@
|
|
|
2
2
|
|
|
3
3
|
require "test_prof"
|
|
4
4
|
|
|
5
|
+
module TestProf
|
|
6
|
+
module Rails
|
|
7
|
+
# Add `with_logging` and `with_ar_logging helpers`
|
|
8
|
+
module LoggingHelpers
|
|
9
|
+
class << self
|
|
10
|
+
attr_writer :logger
|
|
11
|
+
|
|
12
|
+
def logger
|
|
13
|
+
return @logger if instance_variable_defined?(:@logger)
|
|
14
|
+
|
|
15
|
+
@logger = Logger.new(STDOUT)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def ar_loggables
|
|
19
|
+
return @ar_loggables if instance_variable_defined?(:@ar_loggables)
|
|
20
|
+
|
|
21
|
+
@ar_loggables = [
|
|
22
|
+
::ActiveRecord::Base,
|
|
23
|
+
::ActiveSupport::LogSubscriber
|
|
24
|
+
]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
28
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
|
29
|
+
def all_loggables
|
|
30
|
+
return @all_loggables if instance_variable_defined?(:@all_loggables)
|
|
31
|
+
|
|
32
|
+
@all_loggables = [
|
|
33
|
+
::ActiveSupport::LogSubscriber,
|
|
34
|
+
::Rails,
|
|
35
|
+
defined?(::ActiveRecord::Base) && ::ActiveRecord::Base,
|
|
36
|
+
defined?(::ActiveJob::Base) && ::ActiveJob::Base,
|
|
37
|
+
defined?(::ActionView::Base) && ::ActionView::Base,
|
|
38
|
+
defined?(::ActionMailer::Base) && ::ActionMailer::Base,
|
|
39
|
+
defined?(::ActionCable::Server::Base.config) && ::ActionCable::Server::Base.config,
|
|
40
|
+
defined?(::ActiveStorage) && ::ActiveStorage
|
|
41
|
+
].compact
|
|
42
|
+
end
|
|
43
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
44
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
|
45
|
+
|
|
46
|
+
def swap_logger(loggables)
|
|
47
|
+
loggables.map do |loggable|
|
|
48
|
+
was_logger = loggable.logger
|
|
49
|
+
loggable.logger = logger
|
|
50
|
+
was_logger
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def restore_logger(was_loggers, loggables)
|
|
55
|
+
loggables.each_with_index do |loggable, i|
|
|
56
|
+
loggable.logger = was_loggers[i]
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Enable verbose Rails logging within a block
|
|
62
|
+
def with_logging
|
|
63
|
+
*loggers = LoggingHelpers.swap_logger(LoggingHelpers.all_loggables)
|
|
64
|
+
yield
|
|
65
|
+
ensure
|
|
66
|
+
LoggingHelpers.restore_logger(loggers, LoggingHelpers.all_loggables)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def with_ar_logging
|
|
70
|
+
*loggers = LoggingHelpers.swap_logger(LoggingHelpers.ar_loggables)
|
|
71
|
+
yield
|
|
72
|
+
ensure
|
|
73
|
+
LoggingHelpers.restore_logger(loggers, LoggingHelpers.ar_loggables)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
5
79
|
if TestProf.rspec?
|
|
6
|
-
RSpec.shared_context "logging:verbose"
|
|
80
|
+
RSpec.shared_context "logging:verbose" do
|
|
7
81
|
around(:each) do |ex|
|
|
8
|
-
|
|
9
|
-
Rails.logger,
|
|
10
|
-
ActiveRecord::Base.logger
|
|
11
|
-
ActiveSupport::LogSubscriber.logger =
|
|
12
|
-
Rails.logger =
|
|
13
|
-
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
|
14
|
-
ex.run
|
|
15
|
-
ActiveSupport::LogSubscriber.logger,
|
|
16
|
-
Rails.logger,
|
|
17
|
-
ActiveRecord::Base.logger = *loggers
|
|
82
|
+
with_logging(&ex)
|
|
18
83
|
end
|
|
19
84
|
end
|
|
20
85
|
|
|
21
|
-
RSpec.shared_context "logging:active_record"
|
|
86
|
+
RSpec.shared_context "logging:active_record" do
|
|
22
87
|
around(:each) do |ex|
|
|
23
|
-
|
|
24
|
-
ActiveSupport::LogSubscriber.logger
|
|
25
|
-
ActiveSupport::LogSubscriber.logger =
|
|
26
|
-
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
|
27
|
-
ex.run
|
|
28
|
-
ActiveSupport::LogSubscriber.logger,
|
|
29
|
-
ActiveRecord::Base.logger = *loggers
|
|
88
|
+
with_ar_logging(&ex)
|
|
30
89
|
end
|
|
31
90
|
end
|
|
91
|
+
|
|
92
|
+
RSpec.configure do |config|
|
|
93
|
+
config.include TestProf::Rails::LoggingHelpers
|
|
94
|
+
config.include_context "logging:active_record", log: :ar
|
|
95
|
+
config.include_context "logging:verbose", log: true
|
|
96
|
+
end
|
|
32
97
|
end
|
|
33
98
|
|
|
34
99
|
TestProf.activate("LOG", "all") do
|
|
35
100
|
TestProf.log :info, "Rails verbose logging enabled"
|
|
36
101
|
ActiveSupport::LogSubscriber.logger =
|
|
37
102
|
Rails.logger =
|
|
38
|
-
ActiveRecord::Base.logger =
|
|
103
|
+
ActiveRecord::Base.logger = TestProf::Rails::LoggingHelpers.logger
|
|
39
104
|
end
|
|
40
105
|
|
|
41
106
|
TestProf.activate("LOG", "ar") do
|
|
42
107
|
TestProf.log :info, "Active Record verbose logging enabled"
|
|
43
108
|
ActiveSupport::LogSubscriber.logger =
|
|
44
|
-
ActiveRecord::Base.logger =
|
|
109
|
+
ActiveRecord::Base.logger = TestProf::Rails::LoggingHelpers.logger
|
|
45
110
|
end
|
|
@@ -14,7 +14,7 @@ module TestProf
|
|
|
14
14
|
def suites
|
|
15
15
|
# Make sure that sample contains only _real_ suites
|
|
16
16
|
Minitest::Runnable.runnables
|
|
17
|
-
|
|
17
|
+
.reject { |suite| CORE_RUNNABLES.include?(suite) }
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def sample_groups(sample_size)
|
|
@@ -39,10 +39,10 @@ module TestProf
|
|
|
39
39
|
|
|
40
40
|
# Overrides Minitest.run
|
|
41
41
|
def run(*)
|
|
42
|
-
if ENV[
|
|
43
|
-
MinitestSample.sample_examples(ENV[
|
|
44
|
-
elsif ENV[
|
|
45
|
-
MinitestSample.sample_groups(ENV[
|
|
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)
|
|
46
46
|
end
|
|
47
47
|
super
|
|
48
48
|
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require "test_prof/any_fixture"
|
|
4
4
|
require "test_prof/recipes/rspec/before_all"
|
|
5
5
|
|
|
6
|
-
RSpec.shared_context "any_fixture:clean"
|
|
6
|
+
RSpec.shared_context "any_fixture:clean" do
|
|
7
7
|
extend TestProf::BeforeAll::RSpec
|
|
8
8
|
|
|
9
9
|
before_all do
|
|
@@ -12,6 +12,8 @@ RSpec.shared_context "any_fixture:clean", with_clean_fixture: true do
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
RSpec.configure do |config|
|
|
15
|
+
config.include_context "any_fixture:clean", with_clean_fixture: true
|
|
16
|
+
|
|
15
17
|
config.after(:suite) do
|
|
16
18
|
TestProf::AnyFixture.report_stats if TestProf::AnyFixture.reporting_enabled?
|
|
17
19
|
TestProf::AnyFixture.reset
|
|
@@ -9,13 +9,14 @@ module TestProf
|
|
|
9
9
|
def before_all(&block)
|
|
10
10
|
raise ArgumentError, "Block is required!" unless block_given?
|
|
11
11
|
|
|
12
|
-
return
|
|
12
|
+
return within_before_all(&block) if within_before_all?
|
|
13
13
|
|
|
14
14
|
@__before_all_activated__ = true
|
|
15
15
|
|
|
16
16
|
before(:all) do
|
|
17
|
-
BeforeAll.begin_transaction
|
|
18
|
-
|
|
17
|
+
BeforeAll.begin_transaction do
|
|
18
|
+
instance_eval(&block)
|
|
19
|
+
end
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
after(:all) do
|
|
@@ -23,6 +24,14 @@ module TestProf
|
|
|
23
24
|
end
|
|
24
25
|
end
|
|
25
26
|
|
|
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
|
+
|
|
26
35
|
def within_before_all?
|
|
27
36
|
instance_variable_defined?(:@__before_all_activated__)
|
|
28
37
|
end
|
|
@@ -4,7 +4,11 @@ require "test_prof/factory_all_stub"
|
|
|
4
4
|
|
|
5
5
|
TestProf::FactoryAllStub.init
|
|
6
6
|
|
|
7
|
-
RSpec.shared_context "factory:stub"
|
|
7
|
+
RSpec.shared_context "factory:stub" do
|
|
8
8
|
prepend_before(:all) { TestProf::FactoryAllStub.enable! }
|
|
9
9
|
append_after(:all) { TestProf::FactoryAllStub.disable! }
|
|
10
10
|
end
|
|
11
|
+
|
|
12
|
+
RSpec.configure do |config|
|
|
13
|
+
config.include_context "factory:stub", factory: :stub
|
|
14
|
+
end
|
|
@@ -7,18 +7,32 @@ module TestProf
|
|
|
7
7
|
# Just like `let`, but persist the result for the whole group.
|
|
8
8
|
# NOTE: Experimental and magical, for more control use `before_all`.
|
|
9
9
|
module LetItBe
|
|
10
|
+
class Configuration
|
|
11
|
+
# Define an alias for `let_it_be` with the predefined options:
|
|
12
|
+
#
|
|
13
|
+
# TestProf::LetItBe.configure do |config|
|
|
14
|
+
# config.alias_to :let_it_be_reloaded, reload: true
|
|
15
|
+
# end
|
|
16
|
+
def alias_to(name, **default_args)
|
|
17
|
+
LetItBe.define_let_it_be_alias(name, **default_args)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
10
21
|
class << self
|
|
22
|
+
def config
|
|
23
|
+
@config ||= Configuration.new
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def configure
|
|
27
|
+
yield config
|
|
28
|
+
end
|
|
29
|
+
|
|
11
30
|
def module_for(group)
|
|
12
31
|
modules[group] ||= begin
|
|
13
32
|
Module.new.tap { |mod| group.prepend(mod) }
|
|
14
33
|
end
|
|
15
34
|
end
|
|
16
35
|
|
|
17
|
-
# Only works with RSpec 3.2.0
|
|
18
|
-
def supported?
|
|
19
|
-
TestProf::Utils.verify_gem_version('rspec-core', at_least: '3.2.0')
|
|
20
|
-
end
|
|
21
|
-
|
|
22
36
|
private
|
|
23
37
|
|
|
24
38
|
def modules
|
|
@@ -28,20 +42,21 @@ module TestProf
|
|
|
28
42
|
# Use uniq prefix for instance variables to avoid collisions
|
|
29
43
|
# We want to use the power of Ruby's unicode support)
|
|
30
44
|
# And we love cats!)
|
|
31
|
-
PREFIX = RUBY_ENGINE ==
|
|
45
|
+
PREFIX = RUBY_ENGINE == "jruby" ? "@__jruby_is_not_cat_friendly__" : "@😸"
|
|
32
46
|
|
|
33
|
-
def
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return let!(identifier, &block)
|
|
47
|
+
def self.define_let_it_be_alias(name, **default_args)
|
|
48
|
+
define_method(name) do |identifier, **options, &blk|
|
|
49
|
+
let_it_be(identifier, default_args.merge(options), &blk)
|
|
37
50
|
end
|
|
51
|
+
end
|
|
38
52
|
|
|
53
|
+
def let_it_be(identifier, **options, &block)
|
|
39
54
|
initializer = proc do
|
|
40
55
|
instance_variable_set(:"#{PREFIX}#{identifier}", instance_exec(&block))
|
|
41
56
|
end
|
|
42
57
|
|
|
43
58
|
if within_before_all?
|
|
44
|
-
|
|
59
|
+
within_before_all(&initializer)
|
|
45
60
|
else
|
|
46
61
|
before_all(&initializer)
|
|
47
62
|
end
|
|
@@ -72,7 +87,7 @@ module TestProf
|
|
|
72
87
|
LetItBe.module_for(self).module_eval do
|
|
73
88
|
define_method(identifier) do
|
|
74
89
|
# Trying to detect the context (couldn't find other way so far)
|
|
75
|
-
if
|
|
90
|
+
if /\(:context\)/.match?(@__inspect_output)
|
|
76
91
|
instance_variable_get(:"#{PREFIX}#{identifier}")
|
|
77
92
|
else
|
|
78
93
|
# Fallback to let definition
|
|
@@ -10,8 +10,7 @@ module TestProf
|
|
|
10
10
|
|
|
11
11
|
NOTIFICATIONS = %i[
|
|
12
12
|
example_group_finished
|
|
13
|
-
|
|
14
|
-
example_failed
|
|
13
|
+
example_finished
|
|
15
14
|
].freeze
|
|
16
15
|
|
|
17
16
|
def initialize
|
|
@@ -35,9 +34,6 @@ module TestProf
|
|
|
35
34
|
@examples_time += notification.example.execution_result.run_time
|
|
36
35
|
end
|
|
37
36
|
|
|
38
|
-
alias example_passed example_finished
|
|
39
|
-
alias example_failed example_finished
|
|
40
|
-
|
|
41
37
|
def example_group_finished(notification)
|
|
42
38
|
return unless notification.group.top_level?
|
|
43
39
|
|
|
@@ -127,7 +123,7 @@ module TestProf
|
|
|
127
123
|
end
|
|
128
124
|
|
|
129
125
|
# Register RSpecDissect listener
|
|
130
|
-
TestProf.activate(
|
|
126
|
+
TestProf.activate("RD_PROF") do
|
|
131
127
|
RSpec.configure do |config|
|
|
132
128
|
listener = nil
|
|
133
129
|
|
|
@@ -21,10 +21,10 @@ module TestProf
|
|
|
21
21
|
Thread.current[:_rspec_dissect_let_depth] += 1
|
|
22
22
|
begin
|
|
23
23
|
res = if Thread.current[:_rspec_dissect_let_depth] == 1
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
RSpecDissect.track(:let, id) { super }
|
|
25
|
+
else
|
|
26
|
+
super
|
|
27
|
+
end
|
|
28
28
|
ensure
|
|
29
29
|
Thread.current[:_rspec_dissect_let_depth] -= 1
|
|
30
30
|
end
|
|
@@ -45,14 +45,14 @@ module TestProf
|
|
|
45
45
|
|
|
46
46
|
def initialize
|
|
47
47
|
@let_stats_enabled = true
|
|
48
|
-
@let_top_count = (ENV[
|
|
49
|
-
@top_count = (ENV[
|
|
50
|
-
@stamp = ENV[
|
|
51
|
-
@mode = ENV[
|
|
48
|
+
@let_top_count = (ENV["RD_PROF_LET_TOP"] || 3).to_i
|
|
49
|
+
@top_count = (ENV["RD_PROF_TOP"] || 5).to_i
|
|
50
|
+
@stamp = ENV["RD_PROF_STAMP"]
|
|
51
|
+
@mode = ENV["RD_PROF"] == "1" ? "all" : ENV["RD_PROF"]
|
|
52
52
|
|
|
53
53
|
unless MODES.include?(mode)
|
|
54
54
|
raise "Unknown RSpecDissect mode: #{mode};" \
|
|
55
|
-
"available modes: #{MODES.join(
|
|
55
|
+
"available modes: #{MODES.join(", ")}"
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
RSpecStamp.config.tags = @stamp if stamp?
|
|
@@ -87,10 +87,8 @@ module TestProf
|
|
|
87
87
|
def init
|
|
88
88
|
RSpec::Core::Example.prepend(ExampleInstrumentation)
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
RSpec::Core::MemoizedHelpers::NonThreadSafeMemoized.prepend(MemoizedInstrumentation)
|
|
93
|
-
end
|
|
90
|
+
RSpec::Core::MemoizedHelpers::ThreadsafeMemoized.prepend(MemoizedInstrumentation)
|
|
91
|
+
RSpec::Core::MemoizedHelpers::NonThreadSafeMemoized.prepend(MemoizedInstrumentation)
|
|
94
92
|
|
|
95
93
|
@data = {}
|
|
96
94
|
|
|
@@ -100,10 +98,6 @@ module TestProf
|
|
|
100
98
|
|
|
101
99
|
reset!
|
|
102
100
|
|
|
103
|
-
if config.let? && !memoization_available?
|
|
104
|
-
log :warn, "RSpecDissect: `let` profiling is not supported (requires RSpec >= 3.3.0)\n"
|
|
105
|
-
end
|
|
106
|
-
|
|
107
101
|
log :info, "RSpecDissect enabled"
|
|
108
102
|
end
|
|
109
103
|
|
|
@@ -120,7 +114,7 @@ module TestProf
|
|
|
120
114
|
|
|
121
115
|
def reset!
|
|
122
116
|
METRICS.each do |type|
|
|
123
|
-
@data[type.to_s] = {
|
|
117
|
+
@data[type.to_s] = {time: 0.0, meta: []}
|
|
124
118
|
end
|
|
125
119
|
end
|
|
126
120
|
|
|
@@ -148,6 +142,6 @@ require "test_prof/rspec_dissect/collectors/let"
|
|
|
148
142
|
require "test_prof/rspec_dissect/collectors/before"
|
|
149
143
|
require "test_prof/rspec_dissect/rspec" if TestProf.rspec?
|
|
150
144
|
|
|
151
|
-
TestProf.activate(
|
|
145
|
+
TestProf.activate("RD_PROF") do
|
|
152
146
|
TestProf::RSpecDissect.init
|
|
153
147
|
end
|