test-prof 0.1.0.pre5 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +16 -4
- data/assets/flamegraph.demo.html +173 -0
- data/assets/flamegraph.template.html +196 -0
- data/assets/src/d3-tip.js +352 -0
- data/assets/src/d3-tip.min.js +1 -0
- data/assets/src/d3.flameGraph.css +92 -0
- data/assets/src/d3.flameGraph.js +459 -0
- data/assets/src/d3.flameGraph.min.css +1 -0
- data/assets/src/d3.flameGraph.min.js +1 -0
- data/assets/src/d3.v4.min.js +8 -0
- data/guides/any_fixture.md +1 -1
- data/guides/event_prof.md +30 -0
- data/guides/factory_default.md +109 -0
- data/guides/factory_prof.md +85 -0
- data/guides/rubocop.md +48 -0
- data/guides/ruby_prof.md +2 -0
- data/guides/stack_prof.md +5 -1
- data/guides/tag_prof.md +52 -0
- data/guides/tests_sampling.md +24 -0
- data/lib/test_prof.rb +31 -7
- data/lib/test_prof/cops/rspec/aggregate_failures.rb +140 -0
- data/lib/test_prof/event_prof/custom_events.rb +3 -3
- data/lib/test_prof/event_prof/custom_events/factory_create.rb +10 -8
- data/lib/test_prof/event_prof/custom_events/sidekiq_inline.rb +10 -8
- data/lib/test_prof/event_prof/custom_events/sidekiq_jobs.rb +12 -10
- data/lib/test_prof/event_prof/rspec.rb +5 -1
- data/lib/test_prof/factory_default.rb +58 -0
- data/lib/test_prof/factory_default/factory_girl_patch.rb +22 -0
- data/lib/test_prof/factory_doctor.rb +11 -9
- data/lib/test_prof/factory_doctor/rspec.rb +5 -3
- data/lib/test_prof/factory_prof.rb +140 -0
- data/lib/test_prof/factory_prof/factory_girl_patch.rb +12 -0
- data/lib/test_prof/factory_prof/printers/flamegraph.rb +71 -0
- data/lib/test_prof/factory_prof/printers/simple.rb +28 -0
- data/lib/test_prof/recipes/minitest/sample.rb +29 -0
- data/lib/test_prof/recipes/rspec/factory_default.rb +9 -0
- data/lib/test_prof/recipes/rspec/sample.rb +13 -0
- data/lib/test_prof/rspec_stamp/rspec.rb +5 -1
- data/lib/test_prof/rubocop.rb +3 -0
- data/lib/test_prof/ruby_prof.rb +6 -12
- data/lib/test_prof/stack_prof.rb +14 -7
- data/lib/test_prof/tag_prof.rb +8 -0
- data/lib/test_prof/tag_prof/rspec.rb +84 -0
- data/lib/test_prof/version.rb +1 -1
- metadata +48 -41
- data/.gitignore +0 -10
- data/.rspec +0 -2
- data/.rubocop.yml +0 -69
- data/.travis.yml +0 -5
- data/Gemfile +0 -4
- data/Rakefile +0 -8
- data/bin/setup +0 -8
- data/circle.yml +0 -11
- data/spec/integrations/any_fixture_spec.rb +0 -11
- data/spec/integrations/before_all_spec.rb +0 -11
- data/spec/integrations/event_prof_spec.rb +0 -100
- data/spec/integrations/factory_doctor_spec.rb +0 -20
- data/spec/integrations/fixtures/rspec/any_fixture_fixture.rb +0 -37
- data/spec/integrations/fixtures/rspec/before_all_fixture.rb +0 -32
- data/spec/integrations/fixtures/rspec/event_prof_factory_create_fixture.rb +0 -23
- data/spec/integrations/fixtures/rspec/event_prof_fixture.rb +0 -51
- data/spec/integrations/fixtures/rspec/event_prof_sidekiq_fixture.rb +0 -53
- data/spec/integrations/fixtures/rspec/factory_doctor_fixture.rb +0 -33
- data/spec/integrations/fixtures/rspec/rspec_stamp_fixture_tmpl.rb +0 -33
- data/spec/integrations/rspec_stamp_spec.rb +0 -53
- data/spec/spec_helper.rb +0 -38
- data/spec/support/ar_models.rb +0 -43
- data/spec/support/instrumenter_stub.rb +0 -19
- data/spec/support/integration_helpers.rb +0 -13
- data/spec/support/transactional_context.rb +0 -11
- data/spec/test_prof/any_fixture_spec.rb +0 -66
- data/spec/test_prof/event_prof_spec.rb +0 -138
- data/spec/test_prof/ext/float_duration_spec.rb +0 -12
- data/spec/test_prof/factory_doctor_spec.rb +0 -84
- data/spec/test_prof/rspec_stamp/parser_spec.rb +0 -58
- data/spec/test_prof/rspec_stamp_spec.rb +0 -281
- data/spec/test_prof/ruby_prof_spec.rb +0 -109
- data/spec/test_prof/stack_prof_spec.rb +0 -73
- data/spec/test_prof_spec.rb +0 -23
- data/test-prof.gemspec +0 -35
@@ -0,0 +1,140 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rubocop'
|
4
|
+
|
5
|
+
module RuboCop
|
6
|
+
module Cop
|
7
|
+
module RSpec
|
8
|
+
# Rejects and auto-corrects the usage of one-liners examples in favour of
|
9
|
+
# :aggregate_failures feature.
|
10
|
+
#
|
11
|
+
# Example:
|
12
|
+
#
|
13
|
+
# # bad
|
14
|
+
# it { is_expected.to be_success }
|
15
|
+
# it { is_expected.to have_header('X-TOTAL-PAGES', 10) }
|
16
|
+
# it { is_expected.to have_header('X-NEXT-PAGE', 2) }
|
17
|
+
#
|
18
|
+
# # good
|
19
|
+
# it "returns the second page", :aggregate_failures do
|
20
|
+
# is_expected.to be_success
|
21
|
+
# is_expected.to have_header('X-TOTAL-PAGES', 10)
|
22
|
+
# is_expected.to have_header('X-NEXT-PAGE', 2)
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
class AggregateFailures < RuboCop::Cop::Cop
|
26
|
+
# From https://github.com/backus/rubocop-rspec/blob/master/lib/rubocop/rspec/language.rb
|
27
|
+
GROUP_BLOCKS = %i[
|
28
|
+
describe context feature example_group
|
29
|
+
xdescribe xcontext xfeature
|
30
|
+
fdescribe fcontext ffeature
|
31
|
+
].freeze
|
32
|
+
|
33
|
+
EXAMPLE_BLOCKS = %i[
|
34
|
+
it specify example scenario its
|
35
|
+
fit fspecify fexample fscenario focus
|
36
|
+
xit xspecify xexample xscenario ski
|
37
|
+
pending
|
38
|
+
].freeze
|
39
|
+
|
40
|
+
def on_block(node)
|
41
|
+
method, _args, body = *node
|
42
|
+
return unless body&.begin_type?
|
43
|
+
|
44
|
+
_receiver, method_name, _object = *method
|
45
|
+
return unless GROUP_BLOCKS.include?(method_name)
|
46
|
+
|
47
|
+
return if check_node(body)
|
48
|
+
|
49
|
+
add_offense(
|
50
|
+
node,
|
51
|
+
:expression,
|
52
|
+
'Use :aggregate_failures instead of several one-liners.'
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
def autocorrect(node)
|
57
|
+
_method, _args, body = *node
|
58
|
+
iter = body.children.each
|
59
|
+
|
60
|
+
first_example = loop do
|
61
|
+
child = iter.next
|
62
|
+
break child if oneliner?(child)
|
63
|
+
end
|
64
|
+
|
65
|
+
base_indent = " " * first_example.source_range.column
|
66
|
+
|
67
|
+
replacements = [
|
68
|
+
header_from(first_example),
|
69
|
+
body_from(first_example, base_indent)
|
70
|
+
]
|
71
|
+
|
72
|
+
last_example = nil
|
73
|
+
|
74
|
+
loop do
|
75
|
+
child = iter.next
|
76
|
+
break unless oneliner?(child)
|
77
|
+
last_example = child
|
78
|
+
replacements << body_from(child, base_indent)
|
79
|
+
end
|
80
|
+
|
81
|
+
replacements << "#{base_indent}end"
|
82
|
+
|
83
|
+
range = first_example.source_range.begin.join(
|
84
|
+
last_example.source_range.end
|
85
|
+
)
|
86
|
+
|
87
|
+
replacement = replacements.join("\n")
|
88
|
+
|
89
|
+
lambda do |corrector|
|
90
|
+
corrector.replace(range, replacement)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def check_node(node)
|
97
|
+
offenders = 0
|
98
|
+
|
99
|
+
node.children.each do |child|
|
100
|
+
if oneliner?(child)
|
101
|
+
offenders += 1
|
102
|
+
elsif example_node?(child)
|
103
|
+
break if offenders > 1
|
104
|
+
offenders = 0
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
offenders < 2
|
109
|
+
end
|
110
|
+
|
111
|
+
def oneliner?(node)
|
112
|
+
node&.block_type? &&
|
113
|
+
(node.source.lines.size == 1) &&
|
114
|
+
example_node?(node)
|
115
|
+
end
|
116
|
+
|
117
|
+
def example_node?(node)
|
118
|
+
method, _args, _body = *node
|
119
|
+
_receiver, method_name, _object = *method
|
120
|
+
EXAMPLE_BLOCKS.include?(method_name)
|
121
|
+
end
|
122
|
+
|
123
|
+
def header_from(node)
|
124
|
+
method, _args, _body = *node
|
125
|
+
_receiver, method_name, _object = *method
|
126
|
+
%(#{method_name} "works", :aggregate_failures do)
|
127
|
+
end
|
128
|
+
|
129
|
+
def body_from(node, base_indent = '')
|
130
|
+
_method, _args, body = *node
|
131
|
+
"#{base_indent}#{indent}#{body.source}"
|
132
|
+
end
|
133
|
+
|
134
|
+
def indent
|
135
|
+
@indent ||= " " * (config.for_cop('IndentationWidth')['Width'] || 2)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "test_prof/event_prof/custom_events/factory_create"
|
4
|
-
require "test_prof/event_prof/custom_events/sidekiq_inline"
|
5
|
-
require "test_prof/event_prof/custom_events/sidekiq_jobs"
|
3
|
+
require "test_prof/event_prof/custom_events/factory_create"
|
4
|
+
require "test_prof/event_prof/custom_events/sidekiq_inline"
|
5
|
+
require "test_prof/event_prof/custom_events/sidekiq_jobs"
|
@@ -39,13 +39,15 @@ module TestProf::EventProf::CustomEvents
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
42
|
+
TestProf.activate('EVENT_PROF', 'factory.create') do
|
43
|
+
if TestProf.require(
|
44
|
+
'factory_girl',
|
45
|
+
<<~MSG
|
46
|
+
Failed to load FactoryGirl.
|
46
47
|
|
47
|
-
|
48
|
-
|
49
|
-
)
|
50
|
-
|
48
|
+
Make sure that "factory_girl" gem is in your Gemfile.
|
49
|
+
MSG
|
50
|
+
)
|
51
|
+
TestProf::EventProf::CustomEvents::FactoryCreate.setup!
|
52
|
+
end
|
51
53
|
end
|
@@ -36,13 +36,15 @@ module TestProf::EventProf::CustomEvents
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
39
|
+
TestProf.activate('EVENT_PROF', 'sidekiq.inline') do
|
40
|
+
if TestProf.require(
|
41
|
+
'sidekiq/testing',
|
42
|
+
<<~MSG
|
43
|
+
Failed to load Sidekiq.
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
)
|
47
|
-
|
45
|
+
Make sure that "sidekiq" gem is in your Gemfile.
|
46
|
+
MSG
|
47
|
+
)
|
48
|
+
TestProf::EventProf::CustomEvents::SidekiqInline.setup!
|
49
|
+
end
|
48
50
|
end
|
@@ -23,16 +23,18 @@ module TestProf::EventProf::CustomEvents
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
26
|
+
TestProf.activate('EVENT_PROF', 'sidekiq.jobs') do
|
27
|
+
if TestProf.require(
|
28
|
+
'sidekiq/testing',
|
29
|
+
<<~MSG
|
30
|
+
Failed to load Sidekiq.
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
)
|
34
|
-
|
35
|
-
|
36
|
-
|
32
|
+
Make sure that "sidekiq" gem is in your Gemfile.
|
33
|
+
MSG
|
34
|
+
)
|
35
|
+
TestProf::EventProf::CustomEvents::SidekiqJobs.setup!
|
36
|
+
TestProf::EventProf.configure do |config|
|
37
|
+
config.rank_by = :count
|
38
|
+
end
|
37
39
|
end
|
38
40
|
end
|
@@ -87,7 +87,11 @@ TestProf.activate('EVENT_PROF') do
|
|
87
87
|
RSpec.configure do |config|
|
88
88
|
listener = TestProf::EventProf::RSpecListener.new
|
89
89
|
|
90
|
-
config.
|
90
|
+
config.before(:suite) do
|
91
|
+
config.reporter.register_listener(
|
92
|
+
listener, *TestProf::EventProf::RSpecListener::NOTIFICATIONS
|
93
|
+
)
|
94
|
+
end
|
91
95
|
|
92
96
|
config.after(:suite) { listener.print }
|
93
97
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "test_prof/factory_default/factory_girl_patch"
|
4
|
+
|
5
|
+
module TestProf
|
6
|
+
# FactoryDefault allows use to re-use associated objects
|
7
|
+
# in factories implicilty
|
8
|
+
module FactoryDefault
|
9
|
+
module DefaultSyntax # :nodoc:
|
10
|
+
def create_default(name, *args, &block)
|
11
|
+
set_factory_default(
|
12
|
+
name,
|
13
|
+
FactoryGirl.create(name, *args, &block)
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_factory_default(name, obj)
|
18
|
+
FactoryDefault.register(name, obj)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class << self
|
23
|
+
def init
|
24
|
+
FactoryGirl::Syntax::Methods.include DefaultSyntax
|
25
|
+
FactoryGirl.extend DefaultSyntax
|
26
|
+
FactoryGirl::Strategy::Create.prepend StrategyExt
|
27
|
+
FactoryGirl::Strategy::Build.prepend StrategyExt
|
28
|
+
FactoryGirl::Strategy::Stub.prepend StrategyExt
|
29
|
+
|
30
|
+
@store = {}
|
31
|
+
end
|
32
|
+
|
33
|
+
def register(name, obj)
|
34
|
+
store[name] = obj
|
35
|
+
end
|
36
|
+
|
37
|
+
def get(name)
|
38
|
+
store[name]
|
39
|
+
end
|
40
|
+
|
41
|
+
def exists?(name)
|
42
|
+
store.key?(name)
|
43
|
+
end
|
44
|
+
|
45
|
+
def remove(name)
|
46
|
+
store.delete(name)
|
47
|
+
end
|
48
|
+
|
49
|
+
def reset
|
50
|
+
@store.clear
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
attr_reader :store
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TestProf
|
4
|
+
module FactoryDefault # :nodoc: all
|
5
|
+
module RunnerExt
|
6
|
+
refine FactoryGirl::FactoryRunner do
|
7
|
+
def name
|
8
|
+
@name
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
using RunnerExt
|
14
|
+
|
15
|
+
module StrategyExt
|
16
|
+
def association(runner)
|
17
|
+
return super unless FactoryDefault.exists?(runner.name)
|
18
|
+
FactoryDefault.get(runner.name)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -3,8 +3,8 @@
|
|
3
3
|
require "test_prof/factory_doctor/factory_girl_patch"
|
4
4
|
|
5
5
|
module TestProf
|
6
|
-
# FactoryDoctor is a tool that helps you identify
|
7
|
-
#
|
6
|
+
# FactoryDoctor is a tool that helps you identify
|
7
|
+
# tests that perform unnecessary database queries.
|
8
8
|
module FactoryDoctor
|
9
9
|
class Result # :nodoc:
|
10
10
|
attr_reader :count, :time, :queries_count
|
@@ -81,14 +81,16 @@ module TestProf
|
|
81
81
|
def within_factory(strategy)
|
82
82
|
return yield if ignore? || !running? || (strategy != :create)
|
83
83
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
84
|
+
begin
|
85
|
+
ts = Time.now if @depth.zero?
|
86
|
+
@depth += 1
|
87
|
+
@count += 1
|
88
|
+
yield
|
89
|
+
ensure
|
90
|
+
@depth -= 1
|
90
91
|
|
91
|
-
|
92
|
+
@time += (Time.now - ts) if @depth.zero?
|
93
|
+
end
|
92
94
|
end
|
93
95
|
|
94
96
|
private
|
@@ -83,9 +83,11 @@ TestProf.activate('FDOC') do
|
|
83
83
|
RSpec.configure do |config|
|
84
84
|
listener = TestProf::FactoryDoctor::RSpecListener.new
|
85
85
|
|
86
|
-
config.
|
87
|
-
|
88
|
-
|
86
|
+
config.before(:suite) do
|
87
|
+
config.reporter.register_listener(
|
88
|
+
listener, *TestProf::FactoryDoctor::RSpecListener::NOTIFICATIONS
|
89
|
+
)
|
90
|
+
end
|
89
91
|
|
90
92
|
config.after(:suite) { listener.print }
|
91
93
|
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "test_prof/factory_prof/factory_girl_patch"
|
4
|
+
require "test_prof/factory_prof/printers/simple"
|
5
|
+
require "test_prof/factory_prof/printers/flamegraph"
|
6
|
+
|
7
|
+
module TestProf
|
8
|
+
# FactoryProf collects "factory stacks" that can be used to build
|
9
|
+
# flamegraphs or detect most popular factories
|
10
|
+
module FactoryProf
|
11
|
+
# FactoryProf configuration
|
12
|
+
class Configuration
|
13
|
+
attr_accessor :mode
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@mode = ENV['FPROF'] == 'flamegraph' ? :flamegraph : :simple
|
17
|
+
end
|
18
|
+
|
19
|
+
# Whether we want to generate flamegraphs
|
20
|
+
def flamegraph?
|
21
|
+
@mode == :flamegraph
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Result # :nodoc:
|
26
|
+
attr_reader :stacks, :raw_stats
|
27
|
+
|
28
|
+
def initialize(stacks, raw_stats)
|
29
|
+
@stacks = stacks
|
30
|
+
@raw_stats = raw_stats
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns sorted stats
|
34
|
+
def stats
|
35
|
+
return @stats if instance_variable_defined?(:@stats)
|
36
|
+
|
37
|
+
@stats = @raw_stats.values
|
38
|
+
.sort_by { |el| -el[:total] }
|
39
|
+
end
|
40
|
+
|
41
|
+
def total
|
42
|
+
return @total if instance_variable_defined?(:@total)
|
43
|
+
@total = @raw_stats.values.sum { |v| v[:total] }
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def sorted_stats(key)
|
49
|
+
@raw_stats.values
|
50
|
+
.map { |el| [el[:name], el[key]] }
|
51
|
+
.sort_by { |el| -el[1] }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class << self
|
56
|
+
include TestProf::Logging
|
57
|
+
|
58
|
+
def config
|
59
|
+
@config ||= Configuration.new
|
60
|
+
end
|
61
|
+
|
62
|
+
def configure
|
63
|
+
yield config
|
64
|
+
end
|
65
|
+
|
66
|
+
# Patch factory lib, init vars
|
67
|
+
def init
|
68
|
+
@running = false
|
69
|
+
|
70
|
+
log :info, "FactoryProf enabled (#{config.mode} mode)"
|
71
|
+
|
72
|
+
# Monkey-patch FactoryGirl
|
73
|
+
::FactoryGirl::FactoryRunner.prepend(FactoryGirlPatch) if
|
74
|
+
defined?(::FactoryGirl)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Inits FactoryProf and setups at exit hook,
|
78
|
+
# then runs
|
79
|
+
def run
|
80
|
+
init
|
81
|
+
|
82
|
+
printer = config.flamegraph? ? Printers::Flamegraph : Printers::Simple
|
83
|
+
|
84
|
+
at_exit { printer.dump(result) }
|
85
|
+
|
86
|
+
start
|
87
|
+
end
|
88
|
+
|
89
|
+
def start
|
90
|
+
reset!
|
91
|
+
@running = true
|
92
|
+
end
|
93
|
+
|
94
|
+
def stop
|
95
|
+
@running = false
|
96
|
+
end
|
97
|
+
|
98
|
+
def result
|
99
|
+
Result.new(@stacks, @stats)
|
100
|
+
end
|
101
|
+
|
102
|
+
def track(strategy, factory)
|
103
|
+
return yield if !running? || (strategy != :create)
|
104
|
+
begin
|
105
|
+
@depth += 1
|
106
|
+
@current_stack << factory if config.flamegraph?
|
107
|
+
@stats[factory][:total] += 1
|
108
|
+
@stats[factory][:top_level] += 1 if @depth == 1
|
109
|
+
yield
|
110
|
+
ensure
|
111
|
+
@depth -= 1
|
112
|
+
flush_stack if @depth.zero?
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
def reset!
|
119
|
+
@stacks = [] if config.flamegraph?
|
120
|
+
@depth = 0
|
121
|
+
@stats = Hash.new { |h, k| h[k] = { name: k, total: 0, top_level: 0 } }
|
122
|
+
flush_stack
|
123
|
+
end
|
124
|
+
|
125
|
+
def flush_stack
|
126
|
+
return unless config.flamegraph?
|
127
|
+
@stacks << @current_stack unless @current_stack.nil? || @current_stack.empty?
|
128
|
+
@current_stack = []
|
129
|
+
end
|
130
|
+
|
131
|
+
def running?
|
132
|
+
@running == true
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
TestProf.activate('FPROF') do
|
139
|
+
TestProf::FactoryProf.run
|
140
|
+
end
|