test-prof 0.1.0.beta4 → 0.1.0.pre2
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/.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
@@ -0,0 +1,33 @@
|
|
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 "User" do
|
8
|
+
let(:user) { FactoryGirl.create(:user) }
|
9
|
+
|
10
|
+
it "generates random names" do
|
11
|
+
user2 = FactoryGirl.create(:user)
|
12
|
+
expect(user.name).not_to eq user2.name
|
13
|
+
end
|
14
|
+
|
15
|
+
it "validates name" do
|
16
|
+
user.name = ''
|
17
|
+
expect(user).not_to be_valid
|
18
|
+
end
|
19
|
+
|
20
|
+
it "creates and reloads user" do
|
21
|
+
user = FactoryGirl.create(:user, name: 'John')
|
22
|
+
expect(User.find(user.id).name).to eq 'John'
|
23
|
+
end
|
24
|
+
|
25
|
+
it "clones" do
|
26
|
+
expect(user.clone.name).to include("(cloned)")
|
27
|
+
end
|
28
|
+
|
29
|
+
it "is ignored", :fd_ignore do
|
30
|
+
user.name = ''
|
31
|
+
expect(user).not_to be_valid
|
32
|
+
end
|
33
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
|
4
|
+
require "test-prof"
|
5
|
+
require "pry-byebug"
|
6
|
+
require "open3"
|
7
|
+
|
8
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
config.mock_with :rspec
|
12
|
+
|
13
|
+
config.order = :random
|
14
|
+
config.filter_run focus: true
|
15
|
+
config.run_all_when_everything_filtered = true
|
16
|
+
|
17
|
+
config.define_derived_metadata(file_path: %r{/spec/integrations/}) do |metadata|
|
18
|
+
metadata[:type] = :integration
|
19
|
+
end
|
20
|
+
|
21
|
+
config.include IntegrationHelpers, type: :integration
|
22
|
+
|
23
|
+
config.before(:suite) do
|
24
|
+
FileUtils.mkdir_p("tmp")
|
25
|
+
end
|
26
|
+
|
27
|
+
config.before(:each) do
|
28
|
+
allow(TestProf).to receive(:require).and_return(true)
|
29
|
+
# Clear global configuration
|
30
|
+
TestProf.remove_instance_variable(:@config) if
|
31
|
+
TestProf.instance_variable_defined?(:@config)
|
32
|
+
TestProf.config.output = StringIO.new
|
33
|
+
end
|
34
|
+
|
35
|
+
config.after(:suite) do
|
36
|
+
FileUtils.rm_rf("tmp")
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record"
|
4
|
+
require "factory_girl"
|
5
|
+
|
6
|
+
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
|
7
|
+
|
8
|
+
ActiveRecord::Schema.define do
|
9
|
+
create_table :users do |t|
|
10
|
+
t.string :name
|
11
|
+
end
|
12
|
+
|
13
|
+
create_table :posts do |t|
|
14
|
+
t.text :text
|
15
|
+
t.integer :user_id
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class User < ActiveRecord::Base
|
20
|
+
validates :name, presence: true
|
21
|
+
has_many :posts, dependent: :destroy
|
22
|
+
|
23
|
+
def clone
|
24
|
+
copy = dup
|
25
|
+
copy.name = "#{name} (cloned)"
|
26
|
+
copy
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Post < ActiveRecord::Base
|
31
|
+
belongs_to :user
|
32
|
+
end
|
33
|
+
|
34
|
+
FactoryGirl.define do
|
35
|
+
factory :user do
|
36
|
+
name { |n| "John #{n}" }
|
37
|
+
end
|
38
|
+
|
39
|
+
factory :post do
|
40
|
+
text { |n| "Post ##{n}}" }
|
41
|
+
user
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module InstrumenterStub
|
4
|
+
class << self
|
5
|
+
def subscribe(event, &block)
|
6
|
+
listeners[event] = block
|
7
|
+
end
|
8
|
+
|
9
|
+
def notify(event, time)
|
10
|
+
listeners[event].call(time)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def listeners
|
16
|
+
@listeners ||= {}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module IntegrationHelpers
|
4
|
+
def run_rspec(path, env: {})
|
5
|
+
output, status = Open3.capture2(
|
6
|
+
env,
|
7
|
+
"rspec #{path}_fixture.rb",
|
8
|
+
chdir: File.expand_path("../../integrations/fixtures/rspec", __FILE__)
|
9
|
+
)
|
10
|
+
expect(status).to be_success
|
11
|
+
output
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
shared_context "transactional", transactional: true do
|
4
|
+
prepend_before(:each) do
|
5
|
+
ActiveRecord::Base.connection.begin_transaction(joinable: false)
|
6
|
+
end
|
7
|
+
|
8
|
+
append_after(:each) do
|
9
|
+
ActiveRecord::Base.connection.rollback_transaction
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
require "test_prof/any_fixture"
|
5
|
+
|
6
|
+
describe TestProf::AnyFixture, :transactional do
|
7
|
+
subject { described_class }
|
8
|
+
|
9
|
+
after { described_class.reset }
|
10
|
+
|
11
|
+
describe "#register" do
|
12
|
+
it "invokes block only once for the same id" do
|
13
|
+
block = double('block', call: 1)
|
14
|
+
block2 = double('block2', call: 2)
|
15
|
+
|
16
|
+
expect(block).to receive(:call)
|
17
|
+
expect(block2).not_to receive(:call)
|
18
|
+
|
19
|
+
expect(subject.register(:test) { block.call })
|
20
|
+
.to eq 1
|
21
|
+
|
22
|
+
expect(subject.register(:test) { block2.call })
|
23
|
+
.to eq 1
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#clean" do
|
28
|
+
it "tracks AR queries and delete affected tables" do
|
29
|
+
# add a record outside of any fixture to check
|
30
|
+
# that we delete all records from the tables
|
31
|
+
FactoryGirl.create(:user)
|
32
|
+
|
33
|
+
expect do
|
34
|
+
subject.register(:user) { FactoryGirl.create(:user) }
|
35
|
+
end.to change(User, :count).by(1)
|
36
|
+
|
37
|
+
expect do
|
38
|
+
subject.register(:post) { FactoryGirl.create(:post) }
|
39
|
+
end.to change(User, :count).by(1)
|
40
|
+
.and change(Post, :count).by(1)
|
41
|
+
|
42
|
+
subject.clean
|
43
|
+
|
44
|
+
# Try to re-register user - should have no effect
|
45
|
+
subject.register(:user) { FactoryGirl.create(:user) }
|
46
|
+
|
47
|
+
expect(User.count).to eq 0
|
48
|
+
expect(Post.count).to eq 0
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "#reset" do
|
53
|
+
it "delete affected tables and reset cache" do
|
54
|
+
expect do
|
55
|
+
subject.register(:user) { FactoryGirl.create(:user) }
|
56
|
+
end.to change(User, :count).by(1)
|
57
|
+
|
58
|
+
subject.reset
|
59
|
+
expect(User.count).to eq 0
|
60
|
+
|
61
|
+
subject.register(:user) { FactoryGirl.create(:user) }
|
62
|
+
|
63
|
+
expect(User.count).to eq 1
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
describe TestProf::EventProf do
|
6
|
+
# Use fresh config all for every example
|
7
|
+
after { described_class.remove_instance_variable(:@config) }
|
8
|
+
|
9
|
+
before { stub_const("ActiveSupport::Notifications", double(subscribe: nil)) }
|
10
|
+
|
11
|
+
subject { described_class.build }
|
12
|
+
|
13
|
+
describe ".config" do
|
14
|
+
specify "defaults", :aggregate_failiures do
|
15
|
+
expect(subject.top_count).to eq 5
|
16
|
+
expect(subject.rank_by).to eq :time
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe ".build" do
|
21
|
+
before { described_class.config.event = 'test.event' }
|
22
|
+
|
23
|
+
it "subscribes to event" do
|
24
|
+
expect(TestProf::EventProf::Instrumentations::ActiveSupport)
|
25
|
+
.to receive(:subscribe).with('test.event')
|
26
|
+
subject
|
27
|
+
end
|
28
|
+
|
29
|
+
it "sets options" do
|
30
|
+
expect(subject.event).to eq 'test.event'
|
31
|
+
expect(subject.rank_by).to eq :time
|
32
|
+
expect(subject.top_count).to eq 5
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#result" do
|
37
|
+
let(:results) do
|
38
|
+
described_class.config.event = 'test.event'
|
39
|
+
described_class.config.instrumenter = InstrumenterStub
|
40
|
+
|
41
|
+
subject
|
42
|
+
|
43
|
+
subject.group_started 'A'
|
44
|
+
|
45
|
+
subject.example_started 'A1'
|
46
|
+
InstrumenterStub.notify 'test.event', 100
|
47
|
+
subject.example_finished 'A1'
|
48
|
+
|
49
|
+
subject.group_finished 'A'
|
50
|
+
|
51
|
+
subject.group_started 'B'
|
52
|
+
|
53
|
+
subject.example_started 'B1'
|
54
|
+
InstrumenterStub.notify 'test.event', 140
|
55
|
+
InstrumenterStub.notify 'test.event', 240
|
56
|
+
subject.example_finished 'B1'
|
57
|
+
|
58
|
+
subject.example_started 'B2'
|
59
|
+
InstrumenterStub.notify 'test.event', 40
|
60
|
+
subject.example_finished 'B2'
|
61
|
+
|
62
|
+
subject.group_finished 'B'
|
63
|
+
|
64
|
+
subject.group_started 'C'
|
65
|
+
|
66
|
+
subject.example_started 'C1'
|
67
|
+
InstrumenterStub.notify 'test.event', 400
|
68
|
+
InstrumenterStub.notify 'test.event', 40
|
69
|
+
subject.example_finished 'C1'
|
70
|
+
|
71
|
+
subject.example_started 'C2'
|
72
|
+
subject.example_finished 'C2'
|
73
|
+
|
74
|
+
subject.group_finished 'C'
|
75
|
+
|
76
|
+
subject.results
|
77
|
+
end
|
78
|
+
|
79
|
+
it "returns top slow groups and totals" do
|
80
|
+
expect(results).to eq(
|
81
|
+
groups: [
|
82
|
+
{ id: 'C', examples: 2, time: 440, count: 2 },
|
83
|
+
{ id: 'B', examples: 2, time: 420, count: 3 },
|
84
|
+
{ id: 'A', examples: 1, time: 100, count: 1 }
|
85
|
+
]
|
86
|
+
)
|
87
|
+
expect(subject.total_time).to eq 960
|
88
|
+
expect(subject.total_count).to eq 6
|
89
|
+
end
|
90
|
+
|
91
|
+
context "when rank by count" do
|
92
|
+
before { described_class.config.rank_by = :count }
|
93
|
+
|
94
|
+
it "returns top groups by event occurances" do
|
95
|
+
expect(results).to eq(
|
96
|
+
groups: [
|
97
|
+
{ id: 'B', examples: 2, time: 420, count: 3 },
|
98
|
+
{ id: 'C', examples: 2, time: 440, count: 2 },
|
99
|
+
{ id: 'A', examples: 1, time: 100, count: 1 }
|
100
|
+
]
|
101
|
+
)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context "when top_count is specified" do
|
106
|
+
before { described_class.config.top_count = 2 }
|
107
|
+
|
108
|
+
it "returns top groups by event occurances" do
|
109
|
+
expect(results).to eq(
|
110
|
+
groups: [
|
111
|
+
{ id: 'C', examples: 2, time: 440, count: 2 },
|
112
|
+
{ id: 'B', examples: 2, time: 420, count: 3 }
|
113
|
+
]
|
114
|
+
)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context "when per_example is true" do
|
119
|
+
before { described_class.config.per_example = true }
|
120
|
+
|
121
|
+
it "returns top groups and examples" do
|
122
|
+
expect(results).to eq(
|
123
|
+
groups: [
|
124
|
+
{ id: 'C', examples: 2, time: 440, count: 2 },
|
125
|
+
{ id: 'B', examples: 2, time: 420, count: 3 },
|
126
|
+
{ id: 'A', examples: 1, time: 100, count: 1 }
|
127
|
+
],
|
128
|
+
examples: [
|
129
|
+
{ id: 'C1', time: 440, count: 2 },
|
130
|
+
{ id: 'B1', time: 380, count: 2 },
|
131
|
+
{ id: 'A1', time: 100, count: 1 },
|
132
|
+
{ id: 'B2', time: 40, count: 1 }
|
133
|
+
]
|
134
|
+
)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
require "test_prof/ext/float_duration"
|
5
|
+
|
6
|
+
using TestProf::FloatDuration
|
7
|
+
|
8
|
+
describe TestProf::FloatDuration do
|
9
|
+
it "works" do
|
10
|
+
expect((27 * 60 + 41.05142).duration).to eq "27:41.051"
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
# Init FactoryDoctor and patch FactoryGirl
|
6
|
+
TestProf::FactoryDoctor.init
|
7
|
+
|
8
|
+
describe TestProf::FactoryDoctor, :transactional do
|
9
|
+
before { described_class.start }
|
10
|
+
after { described_class.stop }
|
11
|
+
|
12
|
+
# Ensure meta-queries have been performed
|
13
|
+
before(:all) { User.first }
|
14
|
+
|
15
|
+
describe "#result" do
|
16
|
+
subject(:result) { described_class.result }
|
17
|
+
|
18
|
+
it "is not bad when nothing created" do
|
19
|
+
FactoryGirl.build_stubbed(:user)
|
20
|
+
User.first
|
21
|
+
expect(result).not_to be_bad
|
22
|
+
expect(result.count).to eq 0
|
23
|
+
expect(result.time).to eq 0
|
24
|
+
expect(result.queries_count).to eq 1
|
25
|
+
end
|
26
|
+
|
27
|
+
it "detects one useless object" do
|
28
|
+
FactoryGirl.create(:user)
|
29
|
+
expect(result).to be_bad
|
30
|
+
expect(result.count).to eq 1
|
31
|
+
expect(result.time).to be > 0
|
32
|
+
end
|
33
|
+
|
34
|
+
it "detects not useless object when select" do
|
35
|
+
user = FactoryGirl.create(:user)
|
36
|
+
user.reload
|
37
|
+
|
38
|
+
expect(result).not_to be_bad
|
39
|
+
expect(result.count).to eq 1
|
40
|
+
expect(result.queries_count).to eq 1
|
41
|
+
expect(result.time).to be > 0
|
42
|
+
end
|
43
|
+
|
44
|
+
it "detects not useless object when update" do
|
45
|
+
user = FactoryGirl.create(:user)
|
46
|
+
user.update!(name: 'Phil')
|
47
|
+
|
48
|
+
expect(result).not_to be_bad
|
49
|
+
expect(result.count).to eq 1
|
50
|
+
expect(result.queries_count).to eq 1
|
51
|
+
expect(result.time).to be > 0
|
52
|
+
end
|
53
|
+
|
54
|
+
it "detects many objects" do
|
55
|
+
FactoryGirl.create_pair(:user)
|
56
|
+
|
57
|
+
expect(result).to be_bad
|
58
|
+
expect(result.count).to eq 2
|
59
|
+
expect(result.time).to be > 0
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#ignore" do
|
63
|
+
it "does not track create" do
|
64
|
+
described_class.ignore do
|
65
|
+
FactoryGirl.create(:user)
|
66
|
+
end
|
67
|
+
|
68
|
+
expect(result).not_to be_bad
|
69
|
+
expect(result.count).to eq 0
|
70
|
+
expect(result.time).to eq 0
|
71
|
+
end
|
72
|
+
|
73
|
+
it "does not track queries" do
|
74
|
+
user = FactoryGirl.create(:user)
|
75
|
+
|
76
|
+
described_class.ignore { user.reload }
|
77
|
+
|
78
|
+
expect(result).to be_bad
|
79
|
+
expect(result.count).to eq 1
|
80
|
+
expect(result.time).to be > 0
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|