test-prof 1.3.0 → 1.4.4
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 +78 -0
- data/README.md +2 -2
- data/lib/minitest/test_prof_plugin.rb +3 -0
- data/lib/test_prof/any_fixture/dsl.rb +19 -1
- data/lib/test_prof/any_fixture/dump.rb +1 -1
- data/lib/test_prof/any_fixture.rb +16 -2
- data/lib/test_prof/before_all/adapters/active_record.rb +82 -47
- data/lib/test_prof/before_all/isolator.rb +6 -15
- data/lib/test_prof/before_all.rb +31 -8
- data/lib/test_prof/cops/rspec/aggregate_examples/its.rb +2 -2
- data/lib/test_prof/cops/rspec/aggregate_examples/line_range_helpers.rb +1 -1
- data/lib/test_prof/cops/rspec/aggregate_examples/matchers_with_side_effects.rb +1 -1
- data/lib/test_prof/cops/rspec/aggregate_examples/metadata_helpers.rb +1 -1
- data/lib/test_prof/cops/rspec/aggregate_examples/node_matchers.rb +1 -1
- data/lib/test_prof/cops/rspec/aggregate_examples.rb +12 -18
- data/lib/test_prof/core.rb +28 -1
- data/lib/test_prof/event_prof/monitor.rb +3 -10
- data/lib/test_prof/ext/active_record_refind.rb +1 -1
- data/lib/test_prof/ext/string_truncate.rb +1 -1
- data/lib/test_prof/factory_bot.rb +2 -0
- data/lib/test_prof/factory_default/fabrication_patch.rb +60 -0
- data/lib/test_prof/factory_default/factory_bot_patch.rb +59 -9
- data/lib/test_prof/factory_default.rb +11 -39
- data/lib/test_prof/factory_doctor.rb +1 -2
- data/lib/test_prof/factory_prof/fabrication_patch.rb +7 -1
- data/lib/test_prof/factory_prof/factory_bot_patch.rb +15 -1
- data/lib/test_prof/factory_prof/factory_builders/fabrication.rb +2 -2
- data/lib/test_prof/factory_prof/factory_builders/factory_bot.rb +2 -2
- data/lib/test_prof/factory_prof/printers/json.rb +41 -0
- data/lib/test_prof/factory_prof/printers/nate_heckler.rb +1 -1
- data/lib/test_prof/factory_prof/printers/simple.rb +23 -4
- data/lib/test_prof/factory_prof.rb +57 -25
- data/lib/test_prof/memory_prof/printer.rb +2 -0
- data/lib/test_prof/memory_prof/rspec.rb +7 -4
- data/lib/test_prof/memory_prof/tracker/linked_list.rb +8 -6
- data/lib/test_prof/memory_prof/tracker.rb +14 -10
- data/lib/test_prof/recipes/minitest/before_all.rb +2 -2
- data/lib/test_prof/recipes/minitest/sample.rb +14 -5
- data/lib/test_prof/recipes/rspec/any_fixture.rb +8 -0
- data/lib/test_prof/recipes/rspec/let_it_be.rb +19 -0
- data/lib/test_prof/rspec_stamp/parser.rb +1 -1
- data/lib/test_prof/rspec_stamp.rb +7 -5
- data/lib/test_prof/ruby_prof/rspec_no_boot.rb +15 -0
- data/lib/test_prof/ruby_prof.rb +20 -7
- data/lib/test_prof/tag_prof/minitest.rb +74 -0
- data/lib/test_prof/tag_prof/result.rb +2 -2
- data/lib/test_prof/tps_prof/profiler.rb +55 -0
- data/lib/test_prof/tps_prof/reporter/text.rb +48 -0
- data/lib/test_prof/tps_prof/rspec.rb +63 -0
- data/lib/test_prof/tps_prof.rb +46 -0
- data/lib/test_prof/vernier.rb +3 -1
- data/lib/test_prof/version.rb +1 -1
- data/lib/test_prof.rb +1 -0
- metadata +15 -7
@@ -3,6 +3,8 @@
|
|
3
3
|
module TestProf # :nodoc: all
|
4
4
|
FACTORY_GIRL_NAMES = {"factory_bot" => "::FactoryBot", "factory_girl" => "::FactoryGirl"}.freeze
|
5
5
|
|
6
|
+
TestProf.require("active_support")
|
7
|
+
|
6
8
|
FACTORY_GIRL_NAMES.find do |name, cname|
|
7
9
|
TestProf.require(name) do
|
8
10
|
TestProf::FactoryBot = Object.const_get(cname)
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TestProf
|
4
|
+
module FactoryDefault # :nodoc: all
|
5
|
+
module FabricationPatch
|
6
|
+
module DefaultExt
|
7
|
+
def create_default(name, overrides = {}, &block)
|
8
|
+
obj = ::Fabricate.create(name, overrides, &block)
|
9
|
+
set_fabricate_default(name, obj)
|
10
|
+
end
|
11
|
+
|
12
|
+
def set_fabricate_default(name, obj, **opts)
|
13
|
+
FactoryDefault.register(
|
14
|
+
name, obj,
|
15
|
+
preserve_attributes: FactoryDefault.config.preserve_attributes,
|
16
|
+
preserve_traits: FactoryDefault.config.preserve_traits,
|
17
|
+
**opts
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_fabricate_default(name, **overrides)
|
22
|
+
FactoryDefault.get(name, nil, overrides, skip_stats: true)
|
23
|
+
end
|
24
|
+
|
25
|
+
def skip_fabricate_default(&block)
|
26
|
+
FactoryDefault.disable!(&block)
|
27
|
+
end
|
28
|
+
|
29
|
+
def create(name, overrides = {}, &block)
|
30
|
+
self.fabrication_depth += 1
|
31
|
+
# We do not support defaults for objects created with attribute blocks
|
32
|
+
return super if block
|
33
|
+
|
34
|
+
return super if fabrication_depth < 2
|
35
|
+
|
36
|
+
FactoryDefault.get(name, nil, overrides, **{}) ||
|
37
|
+
FactoryDefault.profiler.instrument(name, nil, overrides) { super }
|
38
|
+
ensure
|
39
|
+
self.fabrication_depth -= 1
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def fabrication_depth
|
45
|
+
Thread.current[:_fab_depth_] ||= 0
|
46
|
+
end
|
47
|
+
|
48
|
+
def fabrication_depth=(value)
|
49
|
+
Thread.current[:_fab_depth_] = value
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.patch
|
54
|
+
TestProf.require "fabrication" do
|
55
|
+
::Fabricate.singleton_class.prepend(DefaultExt)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -1,19 +1,69 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "test_prof/factory_bot"
|
4
|
+
|
3
5
|
module TestProf
|
4
6
|
module FactoryDefault # :nodoc: all
|
5
|
-
module
|
6
|
-
|
7
|
-
|
7
|
+
module FactoryBotPatch
|
8
|
+
if defined?(TestProf::FactoryBot::FactoryRunner)
|
9
|
+
module RunnerExt
|
10
|
+
refine TestProf::FactoryBot::FactoryRunner do
|
11
|
+
attr_reader :name, :traits, :overrides
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
using RunnerExt
|
16
|
+
end
|
17
|
+
|
18
|
+
module StrategyExt
|
19
|
+
def association(runner)
|
20
|
+
FactoryDefault.get(runner.name, runner.traits, runner.overrides, **{}) ||
|
21
|
+
FactoryDefault.profiler.instrument(runner.name, runner.traits, runner.overrides) { super }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module SyntaxExt
|
26
|
+
def create_default(name, *args, &block)
|
27
|
+
options = args.extract_options!
|
28
|
+
default_options = {}
|
29
|
+
default_options[:preserve_traits] = options.delete(:preserve_traits) if options.key?(:preserve_traits)
|
30
|
+
default_options[:preserve_attributes] = options.delete(:preserve_attributes) if options.key?(:preserve_attributes)
|
31
|
+
|
32
|
+
obj = TestProf::FactoryBot.create(name, *args, options, &block)
|
33
|
+
|
34
|
+
# Factory with traits
|
35
|
+
name = [name, *args] if args.any?
|
36
|
+
|
37
|
+
set_factory_default(name, obj, **default_options)
|
38
|
+
end
|
39
|
+
|
40
|
+
def set_factory_default(*name, obj, preserve_traits: FactoryDefault.config.preserve_traits, preserve_attributes: FactoryDefault.config.preserve_attributes, **other)
|
41
|
+
name = name.first if name.size == 1
|
42
|
+
FactoryDefault.register(
|
43
|
+
name, obj,
|
44
|
+
preserve_traits: preserve_traits,
|
45
|
+
preserve_attributes: preserve_attributes,
|
46
|
+
**other
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
def get_factory_default(name, *traits, **overrides)
|
51
|
+
FactoryDefault.get(name, traits, overrides, skip_stats: true)
|
52
|
+
end
|
53
|
+
|
54
|
+
def skip_factory_default(&block)
|
55
|
+
FactoryDefault.disable!(&block)
|
56
|
+
end
|
8
57
|
end
|
9
|
-
end
|
10
58
|
|
11
|
-
|
59
|
+
def self.patch
|
60
|
+
return unless defined?(TestProf::FactoryBot)
|
12
61
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
62
|
+
TestProf::FactoryBot::Syntax::Methods.include SyntaxExt
|
63
|
+
TestProf::FactoryBot.extend SyntaxExt
|
64
|
+
TestProf::FactoryBot::Strategy::Create.prepend StrategyExt
|
65
|
+
TestProf::FactoryBot::Strategy::Build.prepend StrategyExt
|
66
|
+
TestProf::FactoryBot::Strategy::Stub.prepend StrategyExt
|
17
67
|
end
|
18
68
|
end
|
19
69
|
end
|
@@ -1,8 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "test_prof/core"
|
4
|
-
|
4
|
+
|
5
5
|
require "test_prof/factory_default/factory_bot_patch"
|
6
|
+
require "test_prof/factory_default/fabrication_patch"
|
7
|
+
|
6
8
|
require "test_prof/ext/float_duration"
|
7
9
|
require "test_prof/ext/active_record_refind" if defined?(::ActiveRecord::Base)
|
8
10
|
|
@@ -144,35 +146,6 @@ module TestProf
|
|
144
146
|
end
|
145
147
|
end
|
146
148
|
|
147
|
-
module DefaultSyntax # :nodoc:
|
148
|
-
def create_default(name, *args, &block)
|
149
|
-
options = args.extract_options!
|
150
|
-
default_options = {}
|
151
|
-
default_options[:preserve_traits] = options.delete(:preserve_traits) if options.key?(:preserve_traits)
|
152
|
-
default_options[:preserve_attributes] = options.delete(:preserve_attributes) if options.key?(:preserve_attributes)
|
153
|
-
|
154
|
-
obj = TestProf::FactoryBot.create(name, *args, options, &block)
|
155
|
-
|
156
|
-
# Factory with traits
|
157
|
-
name = [name, *args] if args.any?
|
158
|
-
|
159
|
-
set_factory_default(name, obj, **default_options)
|
160
|
-
end
|
161
|
-
|
162
|
-
def set_factory_default(name, obj, preserve_traits: FactoryDefault.config.preserve_traits, preserve_attributes: FactoryDefault.config.preserve_attributes, **other)
|
163
|
-
FactoryDefault.register(
|
164
|
-
name, obj,
|
165
|
-
preserve_traits: preserve_traits,
|
166
|
-
preserve_attributes: preserve_attributes,
|
167
|
-
**other
|
168
|
-
)
|
169
|
-
end
|
170
|
-
|
171
|
-
def skip_factory_default(&block)
|
172
|
-
FactoryDefault.disable!(&block)
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
149
|
class Configuration
|
177
150
|
attr_accessor :preserve_traits, :preserve_attributes,
|
178
151
|
:report_summary, :report_stats,
|
@@ -197,11 +170,8 @@ module TestProf
|
|
197
170
|
attr_reader :stats, :profiler
|
198
171
|
|
199
172
|
def init
|
200
|
-
|
201
|
-
|
202
|
-
TestProf::FactoryBot::Strategy::Create.prepend StrategyExt
|
203
|
-
TestProf::FactoryBot::Strategy::Build.prepend StrategyExt
|
204
|
-
TestProf::FactoryBot::Strategy::Stub.prepend StrategyExt
|
173
|
+
FactoryBotPatch.patch
|
174
|
+
FabricationPatch.patch
|
205
175
|
|
206
176
|
@profiler = config.profiling_enabled? ? Profiler.new : NoopProfiler.new
|
207
177
|
@enabled = ENV["FACTORY_DEFAULT_DISABLED"] != "1"
|
@@ -236,7 +206,7 @@ module TestProf
|
|
236
206
|
obj
|
237
207
|
end
|
238
208
|
|
239
|
-
def get(name, traits = nil, overrides = nil)
|
209
|
+
def get(name, traits = nil, overrides = nil, skip_stats: false)
|
240
210
|
return unless enabled?
|
241
211
|
|
242
212
|
record = store[name]
|
@@ -248,7 +218,7 @@ module TestProf
|
|
248
218
|
traits = nil
|
249
219
|
end
|
250
220
|
|
251
|
-
stats[name][:miss] += 1
|
221
|
+
stats[name][:miss] += 1 unless skip_stats
|
252
222
|
|
253
223
|
if traits && !traits.empty? && record[:preserve_traits]
|
254
224
|
return
|
@@ -263,8 +233,10 @@ module TestProf
|
|
263
233
|
end
|
264
234
|
end
|
265
235
|
|
266
|
-
|
267
|
-
|
236
|
+
unless skip_stats
|
237
|
+
stats[name][:miss] -= 1
|
238
|
+
stats[name][:hit] += 1
|
239
|
+
end
|
268
240
|
|
269
241
|
if record[:context] && (record[:context] != :example)
|
270
242
|
object.refind
|
@@ -5,7 +5,13 @@ module TestProf
|
|
5
5
|
# Wrap #run method with FactoryProf tracking
|
6
6
|
module FabricationPatch
|
7
7
|
def create(name, overrides = {})
|
8
|
-
|
8
|
+
variation = ""
|
9
|
+
|
10
|
+
if FactoryProf.config.include_variations? && !overrides.empty?
|
11
|
+
variation += overrides.keys.sort.to_s.gsub(/[\\":]/, "")
|
12
|
+
end
|
13
|
+
|
14
|
+
FactoryBuilders::Fabrication.track(name, variation: variation.to_sym) { super }
|
9
15
|
end
|
10
16
|
end
|
11
17
|
end
|
@@ -5,7 +5,21 @@ module TestProf
|
|
5
5
|
# Wrap #run method with FactoryProf tracking
|
6
6
|
module FactoryBotPatch
|
7
7
|
def run(strategy = @strategy)
|
8
|
-
|
8
|
+
variation = ""
|
9
|
+
|
10
|
+
if FactoryProf.config.include_variations?
|
11
|
+
if @traits || @overrides
|
12
|
+
unless @traits.empty?
|
13
|
+
variation += @traits.sort.join(".").prepend(".")
|
14
|
+
end
|
15
|
+
|
16
|
+
unless @overrides.empty?
|
17
|
+
variation += @overrides.keys.sort.to_s.gsub(/[\\":]/, "")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
FactoryBuilders::FactoryBot.track(strategy, @name, variation: variation.to_sym) { super }
|
9
23
|
end
|
10
24
|
end
|
11
25
|
end
|
@@ -18,9 +18,9 @@ module TestProf
|
|
18
18
|
defined? TestProf::FactoryBot
|
19
19
|
end
|
20
20
|
|
21
|
-
def self.track(strategy, factory, &block)
|
21
|
+
def self.track(strategy, factory, **opts, &block)
|
22
22
|
return yield unless strategy.create?
|
23
|
-
FactoryProf.track(factory, &block)
|
23
|
+
FactoryProf.track(factory, **opts, &block)
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "test_prof/ext/float_duration"
|
4
|
+
|
5
|
+
module TestProf::FactoryProf
|
6
|
+
module Printers
|
7
|
+
module Json # :nodoc: all
|
8
|
+
class << self
|
9
|
+
using TestProf::FloatDuration
|
10
|
+
include TestProf::Logging
|
11
|
+
|
12
|
+
def dump(result, start_time:, **)
|
13
|
+
return log(:info, "No factories detected") if result.raw_stats == {}
|
14
|
+
|
15
|
+
outpath = TestProf.artifact_path("test-prof.result.json")
|
16
|
+
File.write(outpath, convert_stats(result, start_time).to_json)
|
17
|
+
|
18
|
+
log :info, "Profile results to JSON: #{outpath}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def convert_stats(result, start_time)
|
22
|
+
total_run_time = TestProf.now - start_time
|
23
|
+
total_count = result.stats.sum { |stat| stat[:total_count] }
|
24
|
+
total_top_level_count = result.stats.sum { |stat| stat[:top_level_count] }
|
25
|
+
total_time = result.stats.sum { |stat| stat[:top_level_time] }
|
26
|
+
total_uniq_factories = result.stats.map { |stat| stat[:name] }.uniq.count
|
27
|
+
|
28
|
+
{
|
29
|
+
total_count: total_count,
|
30
|
+
total_top_level_count: total_top_level_count,
|
31
|
+
total_time: total_time.duration,
|
32
|
+
total_run_time: total_run_time.duration,
|
33
|
+
total_uniq_factories: total_uniq_factories,
|
34
|
+
|
35
|
+
stats: result.stats
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -10,7 +10,7 @@ module TestProf::FactoryProf
|
|
10
10
|
using TestProf::FloatDuration
|
11
11
|
include TestProf::Logging
|
12
12
|
|
13
|
-
def dump(result, start_time
|
13
|
+
def dump(result, start_time:, **)
|
14
14
|
return if result.raw_stats == {}
|
15
15
|
|
16
16
|
total_time = result.stats.sum { |stat| stat[:top_level_time] }
|
@@ -9,7 +9,7 @@ module TestProf::FactoryProf
|
|
9
9
|
using TestProf::FloatDuration
|
10
10
|
include TestProf::Logging
|
11
11
|
|
12
|
-
def dump(result, start_time:)
|
12
|
+
def dump(result, start_time:, threshold:)
|
13
13
|
return log(:info, "No factories detected") if result.raw_stats == {}
|
14
14
|
msgs = []
|
15
15
|
|
@@ -28,17 +28,36 @@ module TestProf::FactoryProf
|
|
28
28
|
Total time: #{total_time.duration} (out of #{total_run_time.duration})
|
29
29
|
Total uniq factories: #{total_uniq_factories}
|
30
30
|
|
31
|
-
total top-level total time time per call top-level time
|
31
|
+
name total top-level total time time per call top-level time
|
32
32
|
MSG
|
33
33
|
|
34
34
|
result.stats.each do |stat|
|
35
|
-
|
35
|
+
next if stat[:total_count] < threshold
|
36
36
|
|
37
|
-
msgs << format("%8d %11d %13.4fs %17.4fs %18.4fs
|
37
|
+
msgs << format("%-3s%-20s %8d %11d %13.4fs %17.4fs %18.4fs", *format_args(stat))
|
38
|
+
# move other variation ("[...]") to the end of the array
|
39
|
+
sorted_variations = stat[:variations].sort_by.with_index do |variation, i|
|
40
|
+
(variation[:name] == "[...]") ? stat[:variations].size + 1 : i
|
41
|
+
end
|
42
|
+
sorted_variations.each do |variation_stat|
|
43
|
+
next if variation_stat[:total_count] < threshold
|
44
|
+
|
45
|
+
msgs << format("%-5s%-18s %8d %11d %13.4fs %17.4fs %18.4fs", *format_args(variation_stat))
|
46
|
+
end
|
38
47
|
end
|
39
48
|
|
40
49
|
log :info, msgs.join("\n")
|
41
50
|
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def format_args(stat)
|
55
|
+
time_per_call = stat[:total_time] / stat[:total_count]
|
56
|
+
format_args = [""]
|
57
|
+
format_args += stat.values_at(:name, :total_count, :top_level_count, :total_time)
|
58
|
+
format_args << time_per_call
|
59
|
+
format_args << stat[:top_level_time]
|
60
|
+
end
|
42
61
|
end
|
43
62
|
end
|
44
63
|
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
require "test_prof/factory_prof/printers/simple"
|
4
4
|
require "test_prof/factory_prof/printers/flamegraph"
|
5
5
|
require "test_prof/factory_prof/printers/nate_heckler"
|
6
|
+
require "test_prof/factory_prof/printers/json"
|
6
7
|
require "test_prof/factory_prof/factory_builders/factory_bot"
|
7
8
|
require "test_prof/factory_prof/factory_builders/fabrication"
|
8
9
|
|
@@ -15,7 +16,7 @@ module TestProf
|
|
15
16
|
|
16
17
|
# FactoryProf configuration
|
17
18
|
class Configuration
|
18
|
-
attr_accessor :mode, :printer
|
19
|
+
attr_accessor :mode, :printer, :threshold, :include_variations, :variations_limit
|
19
20
|
|
20
21
|
def initialize
|
21
22
|
@mode = (ENV["FPROF"] == "flamegraph") ? :flamegraph : :simple
|
@@ -25,15 +26,24 @@ module TestProf
|
|
25
26
|
Printers::Flamegraph
|
26
27
|
when "nate_heckler"
|
27
28
|
Printers::NateHeckler
|
29
|
+
when "json"
|
30
|
+
Printers::Json
|
28
31
|
else
|
29
32
|
Printers::Simple
|
30
33
|
end
|
34
|
+
@threshold = ENV.fetch("FPROF_THRESHOLD", 0).to_i
|
35
|
+
@include_variations = ENV["FPROF_VARS"] == "1"
|
36
|
+
@variations_limit = ENV.fetch("FPROF_VARIATIONS_LIMIT", 2).to_i
|
31
37
|
end
|
32
38
|
|
33
39
|
# Whether we want to generate flamegraphs
|
34
40
|
def flamegraph?
|
35
41
|
@mode == :flamegraph
|
36
42
|
end
|
43
|
+
|
44
|
+
def include_variations?
|
45
|
+
@include_variations == true
|
46
|
+
end
|
37
47
|
end
|
38
48
|
|
39
49
|
class Result # :nodoc:
|
@@ -46,8 +56,14 @@ module TestProf
|
|
46
56
|
|
47
57
|
# Returns sorted stats
|
48
58
|
def stats
|
49
|
-
@stats ||= @raw_stats.values
|
50
|
-
|
59
|
+
@stats ||= @raw_stats.values.sort_by { |el| -el[:total_count] }.map do |stat|
|
60
|
+
unless stat[:variations].empty?
|
61
|
+
stat = stat.dup
|
62
|
+
stat[:variations] = stat[:variations].values.sort_by { |nested_el| -nested_el[:total_count] }
|
63
|
+
end
|
64
|
+
|
65
|
+
stat
|
66
|
+
end
|
51
67
|
end
|
52
68
|
|
53
69
|
def total_count
|
@@ -57,14 +73,6 @@ module TestProf
|
|
57
73
|
def total_time
|
58
74
|
@total_time ||= @raw_stats.values.sum { |v| v[:total_time] }
|
59
75
|
end
|
60
|
-
|
61
|
-
private
|
62
|
-
|
63
|
-
def sorted_stats(key)
|
64
|
-
@raw_stats.values
|
65
|
-
.map { |el| [el[:name], el[key]] }
|
66
|
-
.sort_by { |el| -el[1] }
|
67
|
-
end
|
68
76
|
end
|
69
77
|
|
70
78
|
class << self
|
@@ -112,7 +120,7 @@ module TestProf
|
|
112
120
|
def print(started_at)
|
113
121
|
printer = config.printer
|
114
122
|
|
115
|
-
printer.dump(result, start_time: started_at)
|
123
|
+
printer.dump(result, start_time: started_at, threshold: config.threshold)
|
116
124
|
end
|
117
125
|
|
118
126
|
def start
|
@@ -128,20 +136,19 @@ module TestProf
|
|
128
136
|
Result.new(@stacks, @stats)
|
129
137
|
end
|
130
138
|
|
131
|
-
def track(factory)
|
139
|
+
def track(factory, variation:)
|
132
140
|
return yield unless running?
|
133
141
|
@depth += 1
|
134
142
|
@current_stack << factory if config.flamegraph?
|
135
|
-
@stats[factory]
|
136
|
-
@stats[factory][:
|
143
|
+
track_count(@stats[factory])
|
144
|
+
track_count(@stats[factory][:variations][variation_name(variation)]) if config.include_variations?
|
137
145
|
t1 = TestProf.now
|
138
146
|
begin
|
139
147
|
yield
|
140
148
|
ensure
|
141
149
|
t2 = TestProf.now
|
142
|
-
|
143
|
-
@stats[factory][:
|
144
|
-
@stats[factory][:top_level_time] += elapsed if @depth == 1
|
150
|
+
track_time(@stats[factory], t1, t2)
|
151
|
+
track_time(@stats[factory][:variations][variation_name(variation)], t1, t2) if config.include_variations?
|
145
152
|
@depth -= 1
|
146
153
|
flush_stack if @depth.zero?
|
147
154
|
end
|
@@ -149,21 +156,46 @@ module TestProf
|
|
149
156
|
|
150
157
|
private
|
151
158
|
|
159
|
+
def variation_name(variation)
|
160
|
+
return "-" if variation.empty?
|
161
|
+
variations_count = variation.to_s.scan(/[\w]+/).size
|
162
|
+
return "[...]" if variations_count > config.variations_limit
|
163
|
+
|
164
|
+
variation
|
165
|
+
end
|
166
|
+
|
152
167
|
def reset!
|
153
168
|
@stacks = [] if config.flamegraph?
|
154
169
|
@depth = 0
|
155
170
|
@stats = Hash.new do |h, k|
|
156
|
-
h[k] =
|
157
|
-
|
158
|
-
|
159
|
-
top_level_count: 0,
|
160
|
-
total_time: 0.0,
|
161
|
-
top_level_time: 0.0
|
162
|
-
}
|
171
|
+
h[k] = hash_template(k)
|
172
|
+
h[k][:variations] = Hash.new { |hh, variation_key| hh[variation_key] = hash_template(variation_key) }
|
173
|
+
h[k]
|
163
174
|
end
|
164
175
|
flush_stack
|
165
176
|
end
|
166
177
|
|
178
|
+
def hash_template(name)
|
179
|
+
{
|
180
|
+
name: name,
|
181
|
+
total_count: 0,
|
182
|
+
top_level_count: 0,
|
183
|
+
total_time: 0.0,
|
184
|
+
top_level_time: 0.0
|
185
|
+
}
|
186
|
+
end
|
187
|
+
|
188
|
+
def track_count(factory)
|
189
|
+
factory[:total_count] += 1
|
190
|
+
factory[:top_level_count] += 1 if @depth == 1
|
191
|
+
end
|
192
|
+
|
193
|
+
def track_time(factory, t1, t2)
|
194
|
+
elapsed = t2 - t1
|
195
|
+
factory[:total_time] += elapsed
|
196
|
+
factory[:top_level_time] += elapsed if @depth == 1
|
197
|
+
end
|
198
|
+
|
167
199
|
def flush_stack
|
168
200
|
return unless config.flamegraph?
|
169
201
|
@stacks << @current_stack unless @current_stack.nil? || @current_stack.empty?
|
@@ -16,23 +16,26 @@ module TestProf
|
|
16
16
|
@tracker = MemoryProf.tracker
|
17
17
|
@printer = MemoryProf.printer(tracker)
|
18
18
|
|
19
|
+
@current_group = nil
|
20
|
+
@current_example = nil
|
21
|
+
|
19
22
|
@tracker.start
|
20
23
|
end
|
21
24
|
|
22
25
|
def example_started(notification)
|
23
|
-
tracker.example_started(example(notification))
|
26
|
+
tracker.example_started(notification.example, example(notification))
|
24
27
|
end
|
25
28
|
|
26
29
|
def example_finished(notification)
|
27
|
-
tracker.example_finished(example
|
30
|
+
tracker.example_finished(notification.example)
|
28
31
|
end
|
29
32
|
|
30
33
|
def example_group_started(notification)
|
31
|
-
tracker.group_started(group(notification))
|
34
|
+
tracker.group_started(notification.group, group(notification))
|
32
35
|
end
|
33
36
|
|
34
37
|
def example_group_finished(notification)
|
35
|
-
tracker.group_finished(group
|
38
|
+
tracker.group_finished(notification.group)
|
36
39
|
end
|
37
40
|
|
38
41
|
def report
|
@@ -52,19 +52,20 @@ module TestProf
|
|
52
52
|
attr_reader :head
|
53
53
|
|
54
54
|
def initialize(memory_at_start)
|
55
|
-
add_node(:total, memory_at_start)
|
55
|
+
add_node(:total, :total, memory_at_start)
|
56
56
|
end
|
57
57
|
|
58
|
-
def add_node(item, memory_at_start)
|
58
|
+
def add_node(id, item, memory_at_start)
|
59
59
|
@head = LinkedListNode.new(
|
60
|
+
id: id,
|
60
61
|
item: item,
|
61
62
|
previous: head,
|
62
63
|
memory_at_start: memory_at_start
|
63
64
|
)
|
64
65
|
end
|
65
66
|
|
66
|
-
def remove_node(
|
67
|
-
return if head.
|
67
|
+
def remove_node(id, memory_at_finish)
|
68
|
+
return if head.id != id
|
68
69
|
head.finish(memory_at_finish)
|
69
70
|
|
70
71
|
current = head
|
@@ -75,9 +76,10 @@ module TestProf
|
|
75
76
|
end
|
76
77
|
|
77
78
|
class LinkedListNode
|
78
|
-
attr_reader :item, :previous, :memory_at_start, :memory_at_finish, :nested_memory
|
79
|
+
attr_reader :id, :item, :previous, :memory_at_start, :memory_at_finish, :nested_memory
|
79
80
|
|
80
|
-
def initialize(item:, memory_at_start:, previous:)
|
81
|
+
def initialize(id:, item:, memory_at_start:, previous:)
|
82
|
+
@id = id
|
81
83
|
@item = item
|
82
84
|
@previous = previous
|
83
85
|
|