test-prof 0.4.9 → 0.5.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 +106 -20
- data/README.md +10 -57
- data/assets/tagprof.demo.html +447 -0
- data/assets/tagprof.template.html +447 -0
- data/lib/minitest/event_prof_formatter.rb +18 -16
- data/lib/test_prof/any_fixture/dsl.rb +18 -0
- data/lib/test_prof/any_fixture.rb +92 -8
- data/lib/test_prof/event_prof/custom_events/factory_create.rb +1 -1
- data/lib/test_prof/event_prof/custom_events/sidekiq_inline.rb +1 -1
- data/lib/test_prof/event_prof/custom_events/sidekiq_jobs.rb +1 -1
- data/lib/test_prof/event_prof/custom_events.rb +30 -0
- data/lib/test_prof/event_prof/minitest.rb +6 -0
- data/lib/test_prof/event_prof/profiler.rb +129 -0
- data/lib/test_prof/event_prof/rspec.rb +20 -11
- data/lib/test_prof/event_prof.rb +10 -108
- data/lib/test_prof/ext/active_record_refind.rb +20 -0
- data/lib/test_prof/factory_all_stub/factory_bot_patch.rb +13 -0
- data/lib/test_prof/factory_all_stub.rb +32 -0
- data/lib/test_prof/factory_doctor/rspec.rb +3 -2
- data/lib/test_prof/factory_prof/printers/flamegraph.rb +9 -13
- data/lib/test_prof/recipes/active_record_one_love.rb +4 -0
- data/lib/test_prof/recipes/active_record_shared_connection.rb +55 -0
- data/lib/test_prof/recipes/logging.rb +37 -0
- data/lib/test_prof/recipes/rspec/any_fixture.rb +4 -1
- data/lib/test_prof/recipes/rspec/factory_all_stub.rb +10 -0
- data/lib/test_prof/rspec_dissect/rspec.rb +4 -2
- data/lib/test_prof/rspec_stamp/rspec.rb +3 -2
- data/lib/test_prof/tag_prof/printers/html.rb +24 -0
- data/lib/test_prof/tag_prof/printers/simple.rb +82 -0
- data/lib/test_prof/tag_prof/result.rb +38 -0
- data/lib/test_prof/tag_prof/rspec.rb +43 -40
- data/lib/test_prof/tag_prof.rb +4 -0
- data/lib/test_prof/utils/html_builder.rb +21 -0
- data/lib/test_prof/version.rb +1 -1
- data/lib/test_prof.rb +2 -1
- metadata +20 -22
- data/assets/logo.svg +0 -1
- data/assets/testprof.png +0 -0
- data/guides/.rubocop.yml +0 -1
- data/guides/any_fixture.md +0 -114
- data/guides/before_all.md +0 -98
- data/guides/event_prof.md +0 -177
- data/guides/factory_default.md +0 -111
- data/guides/factory_doctor.md +0 -119
- data/guides/factory_prof.md +0 -86
- data/guides/let_it_be.md +0 -97
- data/guides/rspec_dissect.md +0 -60
- data/guides/rspec_stamp.md +0 -52
- data/guides/rubocop.md +0 -48
- data/guides/ruby_prof.md +0 -63
- data/guides/stack_prof.md +0 -47
- data/guides/tag_prof.md +0 -51
- data/guides/tests_sampling.md +0 -24
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_prof/factory_bot"
|
|
4
|
+
require "test_prof/factory_all_stub/factory_bot_patch"
|
|
5
|
+
|
|
6
|
+
module TestProf
|
|
7
|
+
# FactoryAllStub inject into FactoryBot to make
|
|
8
|
+
# all strategies be `build_stubbed` strategy.
|
|
9
|
+
module FactoryAllStub
|
|
10
|
+
LOCAL_NAME = :__factory_bot_stub_all__
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def init
|
|
14
|
+
# Monkey-patch FactoryBot / FactoryGirl
|
|
15
|
+
TestProf::FactoryBot::FactoryRunner.prepend(FactoryBotPatch) if
|
|
16
|
+
defined?(TestProf::FactoryBot)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def enabled?
|
|
20
|
+
Thread.current[LOCAL_NAME] == true
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def enable!
|
|
24
|
+
Thread.current[LOCAL_NAME] = true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def disable!
|
|
28
|
+
Thread.current[LOCAL_NAME] = false
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -128,15 +128,16 @@ TestProf.activate('FDOC') do
|
|
|
128
128
|
TestProf::FactoryDoctor.init
|
|
129
129
|
|
|
130
130
|
RSpec.configure do |config|
|
|
131
|
-
listener =
|
|
131
|
+
listener = nil
|
|
132
132
|
|
|
133
133
|
config.before(:suite) do
|
|
134
|
+
listener = TestProf::FactoryDoctor::RSpecListener.new
|
|
134
135
|
config.reporter.register_listener(
|
|
135
136
|
listener, *TestProf::FactoryDoctor::RSpecListener::NOTIFICATIONS
|
|
136
137
|
)
|
|
137
138
|
end
|
|
138
139
|
|
|
139
|
-
config.after(:suite) { listener.print }
|
|
140
|
+
config.after(:suite) { listener.print unless listener.nil? }
|
|
140
141
|
end
|
|
141
142
|
|
|
142
143
|
RSpec.shared_context "factory_doctor:ignore", fd_ignore: true do
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "
|
|
3
|
+
require "test_prof/utils/html_builder"
|
|
4
4
|
|
|
5
5
|
module TestProf::FactoryProf
|
|
6
6
|
module Printers
|
|
7
7
|
module Flamegraph # :nodoc: all
|
|
8
|
+
TEMPLATE = "flamegraph.template.html".freeze
|
|
9
|
+
OUTPUT_NAME = "factory-flame.html".freeze
|
|
10
|
+
|
|
8
11
|
class << self
|
|
9
12
|
include TestProf::Logging
|
|
10
13
|
|
|
@@ -17,7 +20,11 @@ module TestProf::FactoryProf
|
|
|
17
20
|
|
|
18
21
|
report_data[:roots] = convert_stacks(result)
|
|
19
22
|
|
|
20
|
-
path =
|
|
23
|
+
path = TestProf::Utils::HTMLBuilder.generate(
|
|
24
|
+
data: report_data,
|
|
25
|
+
template: TEMPLATE,
|
|
26
|
+
output: OUTPUT_NAME
|
|
27
|
+
)
|
|
21
28
|
|
|
22
29
|
log :info, "FactoryFlame report generated: #{path}"
|
|
23
30
|
end
|
|
@@ -55,17 +62,6 @@ module TestProf::FactoryProf
|
|
|
55
62
|
|
|
56
63
|
res
|
|
57
64
|
end
|
|
58
|
-
|
|
59
|
-
private
|
|
60
|
-
|
|
61
|
-
def generate_html(data)
|
|
62
|
-
template = File.read(TestProf.asset_path("flamegraph.template.html"))
|
|
63
|
-
template.sub! '/**REPORT-DATA**/', data.to_json
|
|
64
|
-
|
|
65
|
-
outpath = TestProf.artifact_path("factory-flame.html")
|
|
66
|
-
File.write(outpath, template)
|
|
67
|
-
outpath
|
|
68
|
-
end
|
|
69
65
|
end
|
|
70
66
|
end
|
|
71
67
|
end
|
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
private
|
|
18
|
+
|
|
19
|
+
def connection=(conn)
|
|
20
|
+
@connection = conn
|
|
21
|
+
connection.singleton_class.prepend Connection
|
|
22
|
+
connection
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
module Connection
|
|
27
|
+
def shared_lock
|
|
28
|
+
@shared_lock ||= Mutex.new
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def exec_cache(*)
|
|
32
|
+
shared_lock.synchronize { super }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def exec_no_cache(*)
|
|
36
|
+
shared_lock.synchronize { super }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def execute(*)
|
|
40
|
+
shared_lock.synchronize { super }
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
module Ext
|
|
45
|
+
def connection
|
|
46
|
+
ActiveRecordSharedConnection.connection || super
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
ActiveSupport.on_load(:active_record) do
|
|
53
|
+
TestProf::ActiveRecordSharedConnection.enable!
|
|
54
|
+
ActiveRecord::Base.singleton_class.prepend TestProf::ActiveRecordSharedConnection::Ext
|
|
55
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
if defined?(RSpec)
|
|
2
|
+
RSpec.shared_context "logging:verbose", log: true do
|
|
3
|
+
around(:each) do |ex|
|
|
4
|
+
*loggers = ActiveSupport::LogSubscriber.logger,
|
|
5
|
+
Rails.logger,
|
|
6
|
+
ActiveRecord::Base.logger
|
|
7
|
+
ActiveSupport::LogSubscriber.logger =
|
|
8
|
+
Rails.logger =
|
|
9
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
|
10
|
+
ex.run
|
|
11
|
+
ActiveSupport::LogSubscriber.logger,
|
|
12
|
+
Rails.logger,
|
|
13
|
+
ActiveRecord::Base.logger = *loggers
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
RSpec.shared_context "logging:active_record", log: :ar do
|
|
18
|
+
around(:each) do |ex|
|
|
19
|
+
logger = ActiveRecord::Base.logger
|
|
20
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
|
21
|
+
ex.run
|
|
22
|
+
ActiveRecord::Base.logger = logger
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
TestProf.activate("LOG", "all") do
|
|
28
|
+
TestProf.log :info, "Rails verbose logging enabled"
|
|
29
|
+
ActiveSupport::LogSubscriber.logger =
|
|
30
|
+
Rails.logger =
|
|
31
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
TestProf.activate("LOG", "ar") do
|
|
35
|
+
TestProf.log :info, "Active Record verbose logging enabled"
|
|
36
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
|
37
|
+
end
|
|
@@ -17,5 +17,8 @@ RSpec.shared_context "any_fixture:clean", with_clean_fixture: true do
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
RSpec.configure do |config|
|
|
20
|
-
config.after(:suite)
|
|
20
|
+
config.after(:suite) do
|
|
21
|
+
TestProf::AnyFixture.report_stats if TestProf::AnyFixture.reporting_enabled?
|
|
22
|
+
TestProf::AnyFixture.reset
|
|
23
|
+
end
|
|
21
24
|
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_prof/factory_all_stub"
|
|
4
|
+
|
|
5
|
+
TestProf::FactoryAllStub.init
|
|
6
|
+
|
|
7
|
+
RSpec.shared_context "factory:stub", factory: :stub do
|
|
8
|
+
prepend_before(:all) { TestProf::FactoryAllStub.enable! }
|
|
9
|
+
append_after(:all) { TestProf::FactoryAllStub.disable! }
|
|
10
|
+
end
|
|
@@ -157,14 +157,16 @@ end
|
|
|
157
157
|
# Register RSpecDissect listener
|
|
158
158
|
TestProf.activate('RD_PROF') do
|
|
159
159
|
RSpec.configure do |config|
|
|
160
|
-
listener =
|
|
160
|
+
listener = nil
|
|
161
161
|
|
|
162
162
|
config.before(:suite) do
|
|
163
|
+
listener = TestProf::RSpecDissect::Listener.new
|
|
164
|
+
|
|
163
165
|
config.reporter.register_listener(
|
|
164
166
|
listener, *TestProf::RSpecDissect::Listener::NOTIFICATIONS
|
|
165
167
|
)
|
|
166
168
|
end
|
|
167
169
|
|
|
168
|
-
config.after(:suite) { listener.print }
|
|
170
|
+
config.after(:suite) { listener.print unless listener.nil? }
|
|
169
171
|
end
|
|
170
172
|
end
|
|
@@ -58,14 +58,15 @@ end
|
|
|
58
58
|
# Register EventProf listener
|
|
59
59
|
TestProf.activate('RSTAMP') do
|
|
60
60
|
RSpec.configure do |config|
|
|
61
|
-
listener =
|
|
61
|
+
listener = nil
|
|
62
62
|
|
|
63
63
|
config.before(:suite) do
|
|
64
|
+
listener = TestProf::RSpecStamp::RSpecListener.new
|
|
64
65
|
config.reporter.register_listener(
|
|
65
66
|
listener, *TestProf::RSpecStamp::RSpecListener::NOTIFICATIONS
|
|
66
67
|
)
|
|
67
68
|
end
|
|
68
69
|
|
|
69
|
-
config.after(:suite) { listener.stamp! }
|
|
70
|
+
config.after(:suite) { listener.stamp! unless listener.nil? }
|
|
70
71
|
end
|
|
71
72
|
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module TestProf::TagProf
|
|
4
|
+
module Printers
|
|
5
|
+
module HTML # :nodoc: all
|
|
6
|
+
TEMPLATE = "tagprof.template.html".freeze
|
|
7
|
+
OUTPUT_NAME = "tag-prof.html".freeze
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
include TestProf::Logging
|
|
11
|
+
|
|
12
|
+
def dump(result)
|
|
13
|
+
path = TestProf::Utils::HTMLBuilder.generate(
|
|
14
|
+
data: result,
|
|
15
|
+
template: TEMPLATE,
|
|
16
|
+
output: OUTPUT_NAME
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
log :info, "TagProf report generated: #{path}"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_prof/ext/float_duration"
|
|
4
|
+
require "test_prof/ext/string_strip_heredoc"
|
|
5
|
+
|
|
6
|
+
module TestProf::TagProf
|
|
7
|
+
module Printers
|
|
8
|
+
module Simple # :nodoc: all
|
|
9
|
+
class << self
|
|
10
|
+
include TestProf::Logging
|
|
11
|
+
using TestProf::StringStripHeredoc
|
|
12
|
+
using TestProf::FloatDuration
|
|
13
|
+
|
|
14
|
+
def dump(result)
|
|
15
|
+
msgs = []
|
|
16
|
+
|
|
17
|
+
msgs <<
|
|
18
|
+
<<-MSG.strip_heredoc
|
|
19
|
+
TagProf report for #{result.tag}
|
|
20
|
+
MSG
|
|
21
|
+
|
|
22
|
+
header = []
|
|
23
|
+
|
|
24
|
+
header << format(
|
|
25
|
+
'%15s %12s ',
|
|
26
|
+
result.tag, 'time'
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
events_format = nil
|
|
30
|
+
|
|
31
|
+
unless result.events.empty?
|
|
32
|
+
events_format = result.events.map { |event| "%#{event.size + 2}s " }.join
|
|
33
|
+
|
|
34
|
+
header << format(
|
|
35
|
+
events_format,
|
|
36
|
+
*result.events
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
header << format(
|
|
41
|
+
'%6s %6s %6s %12s',
|
|
42
|
+
'total', '%total', '%time', 'avg'
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
msgs << header.join
|
|
46
|
+
|
|
47
|
+
msgs << ""
|
|
48
|
+
|
|
49
|
+
total = result.data.values.inject(0) { |acc, v| acc + v[:count] }
|
|
50
|
+
total_time = result.data.values.inject(0) { |acc, v| acc + v[:time] }
|
|
51
|
+
|
|
52
|
+
result.data.values.sort_by { |v| -v[:time] }.each do |tag|
|
|
53
|
+
line = []
|
|
54
|
+
line << format(
|
|
55
|
+
"%15s %12s ",
|
|
56
|
+
tag[:value], tag[:time].duration
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
unless result.events.empty?
|
|
60
|
+
line << format(
|
|
61
|
+
events_format,
|
|
62
|
+
*result.events.map { |event| tag[event].duration }
|
|
63
|
+
)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
line << format(
|
|
67
|
+
"%6d %6.2f %6.2f %12s",
|
|
68
|
+
tag[:count],
|
|
69
|
+
100 * tag[:count].to_f / total,
|
|
70
|
+
100 * tag[:time] / total_time,
|
|
71
|
+
(tag[:time] / tag[:count]).duration
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
msgs << line.join
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
log :info, msgs.join("\n")
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module TestProf
|
|
4
|
+
module TagProf # :nodoc:
|
|
5
|
+
# Object holding all the stats for tags
|
|
6
|
+
class Result
|
|
7
|
+
attr_reader :tag, :data, :events
|
|
8
|
+
|
|
9
|
+
def initialize(tag, events = [])
|
|
10
|
+
@tag = tag
|
|
11
|
+
@events = events
|
|
12
|
+
|
|
13
|
+
@data = Hash.new do |h, k|
|
|
14
|
+
h[k] = { value: k, count: 0, time: 0.0 }
|
|
15
|
+
h[k].merge!(Hash[events.map { |event| [event, 0.0] }]) unless
|
|
16
|
+
events.empty?
|
|
17
|
+
h[k]
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def track(tag, time:, events: {})
|
|
22
|
+
data[tag][:count] += 1
|
|
23
|
+
data[tag][:time] += time
|
|
24
|
+
events.each do |k, v|
|
|
25
|
+
data[tag][k] += v
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_json
|
|
30
|
+
{
|
|
31
|
+
tag: tag,
|
|
32
|
+
data: data.values,
|
|
33
|
+
events: events
|
|
34
|
+
}.to_json
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "test_prof/ext/float_duration"
|
|
4
|
-
require "test_prof/ext/string_strip_heredoc"
|
|
5
|
-
|
|
6
3
|
module TestProf
|
|
7
4
|
module TagProf
|
|
8
5
|
class RSpecListener # :nodoc:
|
|
9
6
|
include Logging
|
|
10
|
-
using FloatDuration
|
|
11
|
-
using StringStripHeredoc
|
|
12
7
|
|
|
13
8
|
NOTIFICATIONS = %i[
|
|
14
9
|
example_started
|
|
@@ -16,58 +11,58 @@ module TestProf
|
|
|
16
11
|
example_passed
|
|
17
12
|
].freeze
|
|
18
13
|
|
|
14
|
+
attr_reader :result, :printer
|
|
15
|
+
|
|
19
16
|
def initialize
|
|
20
|
-
@
|
|
21
|
-
|
|
17
|
+
@printer = ENV['TAG_PROF_FORMAT'] == 'html' ? Printers::HTML : Printers::Simple
|
|
18
|
+
|
|
19
|
+
@result =
|
|
20
|
+
if ENV['TAG_PROF_EVENT'].nil?
|
|
21
|
+
Result.new ENV['TAG_PROF'].to_sym
|
|
22
|
+
else
|
|
23
|
+
require "test_prof/event_prof"
|
|
24
|
+
|
|
25
|
+
@events_profiler = EventProf.build(ENV['TAG_PROF_EVENT'])
|
|
22
26
|
|
|
23
|
-
|
|
27
|
+
Result.new ENV['TAG_PROF'].to_sym, @events_profiler.events
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
log :info, "TagProf enabled (#{result.tag})"
|
|
24
31
|
end
|
|
25
32
|
|
|
26
33
|
def example_started(_notification)
|
|
27
34
|
@ts = TestProf.now
|
|
35
|
+
# enable event profiling
|
|
36
|
+
@events_profiler.group_started(true) if @events_profiler
|
|
28
37
|
end
|
|
29
38
|
|
|
30
39
|
def example_finished(notification)
|
|
31
|
-
tag = notification.example.metadata.fetch(
|
|
40
|
+
tag = notification.example.metadata.fetch(result.tag, :__unknown__)
|
|
41
|
+
|
|
42
|
+
result.track(tag, time: TestProf.now - @ts, events: fetch_events_data)
|
|
32
43
|
|
|
33
|
-
|
|
34
|
-
@
|
|
44
|
+
# reset and disable event profilers
|
|
45
|
+
@events_profiler.group_started(nil) if @events_profiler
|
|
35
46
|
end
|
|
36
47
|
|
|
37
48
|
# NOTE: RSpec < 3.4.0 doesn't have example_finished event
|
|
38
49
|
alias example_passed example_finished
|
|
39
50
|
alias example_failed example_finished
|
|
40
51
|
|
|
41
|
-
def
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
msgs <<
|
|
45
|
-
<<-MSG.strip_heredoc
|
|
46
|
-
TagProf report for #{@tag}
|
|
47
|
-
MSG
|
|
48
|
-
|
|
49
|
-
msgs << format(
|
|
50
|
-
"%15s %12s %6s %6s %6s %12s",
|
|
51
|
-
@tag,
|
|
52
|
-
'time', 'total', '%total', '%time', 'avg'
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
msgs << ""
|
|
52
|
+
def report
|
|
53
|
+
printer.dump(result)
|
|
54
|
+
end
|
|
56
55
|
|
|
57
|
-
|
|
58
|
-
total_time = @tags.values.inject(0) { |acc, v| acc + v[:time] }
|
|
56
|
+
private
|
|
59
57
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
"%15s %12s %6d %6.2f %6.2f %12s",
|
|
63
|
-
tag[:val], tag[:time].duration, tag[:count],
|
|
64
|
-
100 * tag[:count].to_f / total,
|
|
65
|
-
100 * tag[:time] / total_time,
|
|
66
|
-
(tag[:time] / tag[:count]).duration
|
|
67
|
-
)
|
|
68
|
-
end
|
|
58
|
+
def fetch_events_data
|
|
59
|
+
return {} unless @events_profiler
|
|
69
60
|
|
|
70
|
-
|
|
61
|
+
Hash[
|
|
62
|
+
@events_profiler.profilers.map do |profiler|
|
|
63
|
+
[profiler.event, profiler.time]
|
|
64
|
+
end
|
|
65
|
+
]
|
|
71
66
|
end
|
|
72
67
|
end
|
|
73
68
|
end
|
|
@@ -76,14 +71,22 @@ end
|
|
|
76
71
|
# Register TagProf listener
|
|
77
72
|
TestProf.activate('TAG_PROF') do
|
|
78
73
|
RSpec.configure do |config|
|
|
79
|
-
listener =
|
|
74
|
+
listener = nil
|
|
80
75
|
|
|
81
76
|
config.before(:suite) do
|
|
77
|
+
listener = TestProf::TagProf::RSpecListener.new
|
|
82
78
|
config.reporter.register_listener(
|
|
83
79
|
listener, *TestProf::TagProf::RSpecListener::NOTIFICATIONS
|
|
84
80
|
)
|
|
85
81
|
end
|
|
86
82
|
|
|
87
|
-
config.after(:suite) { listener.
|
|
83
|
+
config.after(:suite) { listener.report unless listener.nil? }
|
|
88
84
|
end
|
|
89
85
|
end
|
|
86
|
+
|
|
87
|
+
# Activate custom events
|
|
88
|
+
TestProf.activate('TAG_PROF_EVENT') do
|
|
89
|
+
require "test_prof/event_prof"
|
|
90
|
+
|
|
91
|
+
TestProf::EventProf::CustomEvents.activate_all(ENV['TAG_PROF_EVENT'])
|
|
92
|
+
end
|
data/lib/test_prof/tag_prof.rb
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module TestProf
|
|
6
|
+
module Utils
|
|
7
|
+
# Generates static HTML reports with injected data
|
|
8
|
+
module HTMLBuilder
|
|
9
|
+
class << self
|
|
10
|
+
def generate(data:, template:, output:)
|
|
11
|
+
template = File.read(TestProf.asset_path(template))
|
|
12
|
+
template.sub! '/**REPORT-DATA**/', data.to_json
|
|
13
|
+
|
|
14
|
+
outpath = TestProf.artifact_path(output)
|
|
15
|
+
File.write(outpath, template)
|
|
16
|
+
outpath
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/test_prof/version.rb
CHANGED
data/lib/test_prof.rb
CHANGED
|
@@ -90,7 +90,7 @@ module TestProf
|
|
|
90
90
|
private
|
|
91
91
|
|
|
92
92
|
def activate!(env_var, val)
|
|
93
|
-
yield if ENV[env_var] && (val.nil? || ENV[env_var]
|
|
93
|
+
yield if ENV[env_var] && (val.nil? || val === ENV[env_var])
|
|
94
94
|
end
|
|
95
95
|
|
|
96
96
|
def with_timestamps(path)
|
|
@@ -132,3 +132,4 @@ require "test_prof/factory_prof"
|
|
|
132
132
|
require "test_prof/rspec_stamp"
|
|
133
133
|
require "test_prof/tag_prof"
|
|
134
134
|
require "test_prof/rspec_dissect"
|
|
135
|
+
require "test_prof/factory_all_stub"
|