test-prof 0.1.0.beta4 → 0.1.0.pre2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.rubocop.yml +69 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/README.md +2 -16
- data/Rakefile +8 -0
- data/bin/setup +8 -0
- data/circle.yml +11 -0
- data/guides/any_fixture.md +1 -1
- data/guides/ruby_prof.md +0 -2
- data/guides/stack_prof.md +1 -5
- data/lib/test_prof.rb +6 -31
- data/lib/test_prof/event_prof.rb +4 -2
- data/lib/test_prof/event_prof/custom_events.rb +3 -3
- data/lib/test_prof/event_prof/custom_events/factory_create.rb +9 -11
- data/lib/test_prof/event_prof/custom_events/sidekiq_inline.rb +9 -11
- data/lib/test_prof/event_prof/custom_events/sidekiq_jobs.rb +11 -13
- data/lib/test_prof/event_prof/rspec.rb +1 -5
- data/lib/test_prof/factory_doctor.rb +9 -11
- data/lib/test_prof/factory_doctor/rspec.rb +3 -5
- data/lib/test_prof/ruby_prof.rb +12 -6
- data/lib/test_prof/stack_prof.rb +7 -14
- data/lib/test_prof/version.rb +1 -1
- data/spec/integrations/any_fixture_spec.rb +11 -0
- data/spec/integrations/before_all_spec.rb +11 -0
- data/spec/integrations/event_prof_spec.rb +100 -0
- data/spec/integrations/factory_doctor_spec.rb +20 -0
- data/spec/integrations/fixtures/rspec/any_fixture_fixture.rb +37 -0
- data/spec/integrations/fixtures/rspec/before_all_fixture.rb +32 -0
- data/spec/integrations/fixtures/rspec/event_prof_factory_create_fixture.rb +23 -0
- data/spec/integrations/fixtures/rspec/event_prof_fixture.rb +51 -0
- data/spec/integrations/fixtures/rspec/event_prof_sidekiq_fixture.rb +54 -0
- data/spec/integrations/fixtures/rspec/factory_doctor_fixture.rb +33 -0
- data/spec/spec_helper.rb +38 -0
- data/spec/support/ar_models.rb +43 -0
- data/spec/support/instrumenter_stub.rb +19 -0
- data/spec/support/integration_helpers.rb +13 -0
- data/spec/support/transactional_context.rb +11 -0
- data/spec/test_prof/any_fixture_spec.rb +66 -0
- data/spec/test_prof/event_prof_spec.rb +138 -0
- data/spec/test_prof/ext/float_duration_spec.rb +12 -0
- data/spec/test_prof/factory_doctor_spec.rb +84 -0
- data/spec/test_prof/ruby_prof_spec.rb +109 -0
- data/spec/test_prof/stack_prof_spec.rb +73 -0
- data/spec/test_prof_spec.rb +23 -0
- data/test-prof.gemspec +35 -0
- metadata +34 -49
- data/CHANGELOG.md +0 -7
- data/assets/flamegraph.demo.html +0 -173
- data/assets/flamegraph.template.html +0 -196
- data/assets/src/d3-tip.js +0 -352
- data/assets/src/d3-tip.min.js +0 -1
- data/assets/src/d3.flameGraph.css +0 -92
- data/assets/src/d3.flameGraph.js +0 -459
- data/assets/src/d3.flameGraph.min.css +0 -1
- data/assets/src/d3.flameGraph.min.js +0 -1
- data/assets/src/d3.v4.min.js +0 -8
- data/guides/factory_default.md +0 -109
- data/guides/factory_prof.md +0 -85
- data/guides/rspec_stamp.md +0 -53
- data/guides/rubocop.md +0 -48
- data/guides/tag_prof.md +0 -52
- data/guides/tests_sampling.md +0 -24
- data/lib/test_prof/cops/rspec/aggregate_failures.rb +0 -140
- data/lib/test_prof/factory_default.rb +0 -58
- data/lib/test_prof/factory_default/factory_girl_patch.rb +0 -22
- data/lib/test_prof/factory_prof.rb +0 -140
- data/lib/test_prof/factory_prof/factory_girl_patch.rb +0 -12
- data/lib/test_prof/factory_prof/printers/flamegraph.rb +0 -71
- data/lib/test_prof/factory_prof/printers/simple.rb +0 -28
- data/lib/test_prof/recipes/minitest/sample.rb +0 -29
- data/lib/test_prof/recipes/rspec/factory_default.rb +0 -9
- data/lib/test_prof/recipes/rspec/sample.rb +0 -13
- data/lib/test_prof/rspec_stamp.rb +0 -135
- data/lib/test_prof/rspec_stamp/parser.rb +0 -103
- data/lib/test_prof/rspec_stamp/rspec.rb +0 -95
- data/lib/test_prof/rubocop.rb +0 -3
- data/lib/test_prof/tag_prof.rb +0 -8
- data/lib/test_prof/tag_prof/rspec.rb +0 -84
@@ -83,11 +83,9 @@ TestProf.activate('FDOC') do
|
|
83
83
|
RSpec.configure do |config|
|
84
84
|
listener = TestProf::FactoryDoctor::RSpecListener.new
|
85
85
|
|
86
|
-
config.
|
87
|
-
|
88
|
-
|
89
|
-
)
|
90
|
-
end
|
86
|
+
config.reporter.register_listener(
|
87
|
+
listener, *TestProf::FactoryDoctor::RSpecListener::NOTIFICATIONS
|
88
|
+
)
|
91
89
|
|
92
90
|
config.after(:suite) { listener.print }
|
93
91
|
end
|
data/lib/test_prof/ruby_prof.rb
CHANGED
@@ -48,8 +48,8 @@ module TestProf
|
|
48
48
|
:include_threads, :eliminate_methods
|
49
49
|
|
50
50
|
def initialize
|
51
|
-
@printer =
|
52
|
-
@mode =
|
51
|
+
@printer = :call_stack
|
52
|
+
@mode = :wall
|
53
53
|
@min_percent = 1
|
54
54
|
@include_threads = false
|
55
55
|
@eliminate_methods = ELIMINATE_METHODS
|
@@ -65,10 +65,13 @@ module TestProf
|
|
65
65
|
end
|
66
66
|
|
67
67
|
# Returns an array of printer type (ID) and class.
|
68
|
+
# Takes ENV variable TEST_RUBY_PROF_PRINTER into account.
|
68
69
|
def resolve_printer
|
69
|
-
|
70
|
+
type = ENV['TEST_RUBY_PROF_PRINTER'] || printer
|
70
71
|
|
71
|
-
type
|
72
|
+
return ['custom', type] if type.is_a?(Module)
|
73
|
+
|
74
|
+
type = type.to_s
|
72
75
|
|
73
76
|
raise ArgumentError, "Unknown printer: #{type}" unless
|
74
77
|
PRINTERS.key?(type)
|
@@ -107,8 +110,11 @@ module TestProf
|
|
107
110
|
private
|
108
111
|
|
109
112
|
def build_path(name, printer)
|
110
|
-
TestProf.
|
111
|
-
|
113
|
+
TestProf.with_timestamps(
|
114
|
+
File.join(
|
115
|
+
TestProf.config.output_dir,
|
116
|
+
"ruby-prof-report-#{printer}-#{config.mode}-#{name}.html"
|
117
|
+
)
|
112
118
|
)
|
113
119
|
end
|
114
120
|
|
data/lib/test_prof/stack_prof.rb
CHANGED
@@ -25,8 +25,8 @@ module TestProf
|
|
25
25
|
attr_accessor :mode, :interval, :raw
|
26
26
|
|
27
27
|
def initialize
|
28
|
-
@mode =
|
29
|
-
@raw =
|
28
|
+
@mode = :wall
|
29
|
+
@raw = false
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
@@ -83,23 +83,16 @@ module TestProf
|
|
83
83
|
::StackProf.results(path)
|
84
84
|
|
85
85
|
log :info, "StackProf report generated: #{path}"
|
86
|
-
|
87
|
-
return unless config.raw
|
88
|
-
|
89
|
-
html_path = path.gsub(/\.dump$/, '.html')
|
90
|
-
|
91
|
-
log :info, <<~MSG
|
92
|
-
Run the following command to generate a flame graph report:
|
93
|
-
|
94
|
-
stackprof --flamegraph #{path} > #{html_path} && stackprof --flamegraph-viewer=#{html_path}
|
95
|
-
MSG
|
96
86
|
end
|
97
87
|
|
98
88
|
private
|
99
89
|
|
100
90
|
def build_path(name)
|
101
|
-
TestProf.
|
102
|
-
|
91
|
+
TestProf.with_timestamps(
|
92
|
+
File.join(
|
93
|
+
TestProf.config.output_dir,
|
94
|
+
"stack-prof-report-#{config.mode}-#{name}.dump"
|
95
|
+
)
|
103
96
|
)
|
104
97
|
end
|
105
98
|
|
data/lib/test_prof/version.rb
CHANGED
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
describe "EventProf" do
|
6
|
+
specify "RSpec integration", :aggregate_failures do
|
7
|
+
output = run_rspec('event_prof', env: { 'EVENT_PROF' => 'test.event' })
|
8
|
+
|
9
|
+
expect(output).to include("EventProf results for test.event")
|
10
|
+
expect(output).to match(/Total time: 25:56\.\d{3}/)
|
11
|
+
expect(output).to include("Total events: 8")
|
12
|
+
|
13
|
+
expect(output).to include("Top 5 slowest suites (by time):")
|
14
|
+
expect(output).to include("Top 5 slowest tests (by time):")
|
15
|
+
|
16
|
+
expect(output).to include(
|
17
|
+
"Another something (./event_prof_fixture.rb:42) – 16:40.000 (1 / 2)\n"\
|
18
|
+
"Something (./event_prof_fixture.rb:21) – 09:16.100 (7 / 3)"
|
19
|
+
)
|
20
|
+
|
21
|
+
expect(output).to include(
|
22
|
+
"do very long (./event_prof_fixture.rb:47) – 16:40.000 (1)\n"\
|
23
|
+
"invokes twice (./event_prof_fixture.rb:27) – 06:20.000 (2)\n"\
|
24
|
+
"invokes many times (./event_prof_fixture.rb:33) – 02:16.000 (4)\n"\
|
25
|
+
"invokes once (./event_prof_fixture.rb:22) – 00:40.100 (1)"
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
specify "RSpec integration with rank by count", :aggregate_failures do
|
30
|
+
output = run_rspec(
|
31
|
+
'event_prof',
|
32
|
+
env: { 'EVENT_PROF' => 'test.event', 'EVENT_PROF_RANK' => 'count' }
|
33
|
+
)
|
34
|
+
|
35
|
+
expect(output).to include("EventProf results for test.event")
|
36
|
+
expect(output).to match(/Total time: 25:56\.\d{3}/)
|
37
|
+
expect(output).to include("Total events: 8")
|
38
|
+
|
39
|
+
expect(output).to include("Top 5 slowest suites (by count):")
|
40
|
+
expect(output).to include("Top 5 slowest tests (by count):")
|
41
|
+
|
42
|
+
expect(output).to include(
|
43
|
+
"Something (./event_prof_fixture.rb:21) – 09:16.100 (7 / 3)\n"\
|
44
|
+
"Another something (./event_prof_fixture.rb:42) – 16:40.000 (1 / 2)"
|
45
|
+
)
|
46
|
+
|
47
|
+
expect(output).to include(
|
48
|
+
"invokes many times (./event_prof_fixture.rb:33) – 02:16.000 (4)\n"\
|
49
|
+
"invokes twice (./event_prof_fixture.rb:27) – 06:20.000 (2)\n"\
|
50
|
+
"invokes once (./event_prof_fixture.rb:22) – 00:40.100 (1)\n"\
|
51
|
+
"do very long (./event_prof_fixture.rb:47) – 16:40.000 (1)"
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
context "CustomEvents" do
|
56
|
+
it "works with factory.create" do
|
57
|
+
output = run_rspec(
|
58
|
+
'event_prof_factory_create',
|
59
|
+
env: { 'EVENT_PROF' => 'factory.create' }
|
60
|
+
)
|
61
|
+
|
62
|
+
expect(output).to include("EventProf results for factory.create")
|
63
|
+
expect(output).to match(/Total time: \d{2}:\d{2}\.\d{3}/)
|
64
|
+
expect(output).to include("Total events: 3")
|
65
|
+
|
66
|
+
expect(output).to match(%r{Post \(./event_prof_factory_create_fixture.rb:7\) – \d{2}:\d{2}.\d{3} \(2 / 1\)})
|
67
|
+
expect(output).to match(%r{User \(./event_prof_factory_create_fixture.rb:16\) – \d{2}:\d{2}.\d{3} \(1 / 1\)})
|
68
|
+
end
|
69
|
+
|
70
|
+
it "works with sidekiq.inline" do
|
71
|
+
output = run_rspec(
|
72
|
+
'event_prof_sidekiq',
|
73
|
+
env: { 'EVENT_PROF' => 'sidekiq.inline' }
|
74
|
+
)
|
75
|
+
|
76
|
+
expect(output).to include("EventProf results for sidekiq.inline")
|
77
|
+
expect(output).to match(/Total time: \d{2}:\d{2}\.\d{3}/)
|
78
|
+
expect(output).to include("Total events: 3")
|
79
|
+
|
80
|
+
expect(output).to match(%r{SingleJob \(./event_prof_sidekiq_fixture.rb:28\) – \d{2}:\d{2}.\d{3} \(2 / 2\)})
|
81
|
+
expect(output).to match(%r{BatchJob \(./event_prof_sidekiq_fixture.rb:40\) – \d{2}:\d{2}.\d{3} \(1 / 2\)})
|
82
|
+
end
|
83
|
+
|
84
|
+
it "works with sidekiq.jobs" do
|
85
|
+
output = run_rspec(
|
86
|
+
'event_prof_sidekiq',
|
87
|
+
env: { 'EVENT_PROF' => 'sidekiq.jobs' }
|
88
|
+
)
|
89
|
+
|
90
|
+
expect(output).to include("EventProf results for sidekiq.jobs")
|
91
|
+
expect(output).to match(/Total time: \d{2}:\d{2}\.\d{3}/)
|
92
|
+
expect(output).to include("Total events: 6")
|
93
|
+
|
94
|
+
expect(output).to include("Top 5 slowest suites (by count):")
|
95
|
+
|
96
|
+
expect(output).to match(%r{SingleJob \(./event_prof_sidekiq_fixture.rb:28\) – \d{2}:\d{2}.\d{3} \(2 / 2\)})
|
97
|
+
expect(output).to match(%r{BatchJob \(./event_prof_sidekiq_fixture.rb:40\) – \d{2}:\d{2}.\d{3} \(4 / 2\)})
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
describe "FactoryDoctor" do
|
6
|
+
specify "RSpec integration", :aggregate_failures do
|
7
|
+
output = run_rspec('factory_doctor', env: { 'FDOC' => '1' })
|
8
|
+
|
9
|
+
expect(output).to include("FactoryDoctor report")
|
10
|
+
expect(output).to include("Total (potentially) bad examples: 3")
|
11
|
+
expect(output).to match(/Total wasted time: \d{2}:\d{2}\.\d{3}/)
|
12
|
+
|
13
|
+
expect(output).to include("User (./factory_doctor_fixture.rb:7)")
|
14
|
+
expect(output).to include("generates random names (./factory_doctor_fixture.rb:10) – 2 records created")
|
15
|
+
expect(output).to include("validates name (./factory_doctor_fixture.rb:15) – 1 record created")
|
16
|
+
expect(output).to include("clones (./factory_doctor_fixture.rb:25) – 1 record created")
|
17
|
+
expect(output).not_to include("is ignored")
|
18
|
+
expect(output).not_to include("creates and reloads user")
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path("../../../../lib", __FILE__)
|
4
|
+
require_relative "../../../support/ar_models"
|
5
|
+
require_relative "../../../support/transactional_context"
|
6
|
+
require "test_prof/recipes/rspec/any_fixture"
|
7
|
+
|
8
|
+
shared_context "user", user: true do
|
9
|
+
before(:all) do
|
10
|
+
@user = TestProf::AnyFixture.register(:user) do
|
11
|
+
FactoryGirl.create(:user)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:user) { User.find(@user.id) }
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "User", :user do
|
19
|
+
it "creates user" do
|
20
|
+
user.name = ''
|
21
|
+
expect(user).not_to be_valid
|
22
|
+
end
|
23
|
+
|
24
|
+
context "with clean fixture", :transactional, :with_clean_fixture do
|
25
|
+
specify "no users" do
|
26
|
+
expect(User.count).to eq 0
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "Post", :user do
|
32
|
+
let(:post) { FactoryGirl.create(:post, user: user) }
|
33
|
+
|
34
|
+
it "creates post with the same user" do
|
35
|
+
expect { post }.not_to change(User, :count)
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path("../../../../lib", __FILE__)
|
4
|
+
require_relative "../../../support/ar_models"
|
5
|
+
require "test_prof/recipes/rspec/before_all"
|
6
|
+
|
7
|
+
describe "User" do
|
8
|
+
context "with before_all" do
|
9
|
+
before_all do
|
10
|
+
@user = FactoryGirl.create(:user)
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:user) { User.find(@user.id) }
|
14
|
+
|
15
|
+
it "validates name" do
|
16
|
+
user.name = ''
|
17
|
+
expect(user).not_to be_valid
|
18
|
+
end
|
19
|
+
|
20
|
+
it "clones" do
|
21
|
+
cloned = user.clone
|
22
|
+
cloned.save!
|
23
|
+
expect(cloned.reload.name).to include("(cloned)")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "without before_all" do
|
28
|
+
specify "no users" do
|
29
|
+
expect(User.count).to eq 0
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path("../../../../lib", __FILE__)
|
4
|
+
require_relative "../../../support/ar_models"
|
5
|
+
require "test-prof"
|
6
|
+
|
7
|
+
describe "Post" do
|
8
|
+
let(:user) { FactoryGirl.create(:user) }
|
9
|
+
|
10
|
+
it "generates random names" do
|
11
|
+
user2 = FactoryGirl.create(:post).user
|
12
|
+
expect(user.name).not_to eq user2.name
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "User" do
|
17
|
+
let(:user) { FactoryGirl.create(:user) }
|
18
|
+
|
19
|
+
it "validates name" do
|
20
|
+
user.name = ''
|
21
|
+
expect(user).not_to be_valid
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path("../../../../lib", __FILE__)
|
4
|
+
require "active_support"
|
5
|
+
require "test-prof"
|
6
|
+
|
7
|
+
TestProf::EventProf.configure do |config|
|
8
|
+
config.per_example = true
|
9
|
+
end
|
10
|
+
|
11
|
+
module Instrumenter
|
12
|
+
def self.notify(_event, time)
|
13
|
+
ActiveSupport::Notifications.publish(
|
14
|
+
'test.event',
|
15
|
+
0,
|
16
|
+
time
|
17
|
+
)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "Something" do
|
22
|
+
it "invokes once" do
|
23
|
+
Instrumenter.notify 'test.event', 40.1
|
24
|
+
expect(true).to eq true
|
25
|
+
end
|
26
|
+
|
27
|
+
it "invokes twice" do
|
28
|
+
Instrumenter.notify 'test.event', 140
|
29
|
+
Instrumenter.notify 'test.event', 240
|
30
|
+
expect(true).to eq true
|
31
|
+
end
|
32
|
+
|
33
|
+
it "invokes many times" do
|
34
|
+
Instrumenter.notify 'test.event', 14
|
35
|
+
Instrumenter.notify 'test.event', 40
|
36
|
+
Instrumenter.notify 'test.event', 42
|
37
|
+
Instrumenter.notify 'test.event', 40
|
38
|
+
expect(true).to eq true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "Another something" do
|
43
|
+
it "do nothing" do
|
44
|
+
expect(true).to eq true
|
45
|
+
end
|
46
|
+
|
47
|
+
it "do very long" do
|
48
|
+
Instrumenter.notify 'test.event', 1000
|
49
|
+
expect(true).to eq true
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path("../../../../lib", __FILE__)
|
4
|
+
require "active_support"
|
5
|
+
require "sidekiq/testing"
|
6
|
+
|
7
|
+
Sidekiq::Testing.inline!
|
8
|
+
|
9
|
+
class SingleJob
|
10
|
+
include Sidekiq::Worker
|
11
|
+
|
12
|
+
def perform(*args)
|
13
|
+
1 == 1
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
class BatchJob
|
19
|
+
include Sidekiq::Worker
|
20
|
+
|
21
|
+
def perform(count)
|
22
|
+
count.times { SingleJob.perform_async(true) }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "test-prof"
|
27
|
+
|
28
|
+
describe "SingleJob" do
|
29
|
+
it "invokes once" do
|
30
|
+
SingleJob.perform_async(1)
|
31
|
+
expect(true).to eq true
|
32
|
+
end
|
33
|
+
|
34
|
+
it "invokes twice" do
|
35
|
+
SingleJob.perform_async(2)
|
36
|
+
expect(true).to eq true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "BatchJob" do
|
41
|
+
it "invokes nested" do
|
42
|
+
BatchJob.perform_async(3)
|
43
|
+
expect(true).to eq true
|
44
|
+
end
|
45
|
+
|
46
|
+
context "when fake" do
|
47
|
+
it "is fake" do
|
48
|
+
Sidekiq::Testing.fake! do
|
49
|
+
BatchJob.perform_async(3)
|
50
|
+
end
|
51
|
+
expect(true).to eq true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|