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.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +10 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +69 -0
  5. data/.travis.yml +5 -0
  6. data/Gemfile +4 -0
  7. data/README.md +2 -16
  8. data/Rakefile +8 -0
  9. data/bin/setup +8 -0
  10. data/circle.yml +11 -0
  11. data/guides/any_fixture.md +1 -1
  12. data/guides/ruby_prof.md +0 -2
  13. data/guides/stack_prof.md +1 -5
  14. data/lib/test_prof.rb +6 -31
  15. data/lib/test_prof/event_prof.rb +4 -2
  16. data/lib/test_prof/event_prof/custom_events.rb +3 -3
  17. data/lib/test_prof/event_prof/custom_events/factory_create.rb +9 -11
  18. data/lib/test_prof/event_prof/custom_events/sidekiq_inline.rb +9 -11
  19. data/lib/test_prof/event_prof/custom_events/sidekiq_jobs.rb +11 -13
  20. data/lib/test_prof/event_prof/rspec.rb +1 -5
  21. data/lib/test_prof/factory_doctor.rb +9 -11
  22. data/lib/test_prof/factory_doctor/rspec.rb +3 -5
  23. data/lib/test_prof/ruby_prof.rb +12 -6
  24. data/lib/test_prof/stack_prof.rb +7 -14
  25. data/lib/test_prof/version.rb +1 -1
  26. data/spec/integrations/any_fixture_spec.rb +11 -0
  27. data/spec/integrations/before_all_spec.rb +11 -0
  28. data/spec/integrations/event_prof_spec.rb +100 -0
  29. data/spec/integrations/factory_doctor_spec.rb +20 -0
  30. data/spec/integrations/fixtures/rspec/any_fixture_fixture.rb +37 -0
  31. data/spec/integrations/fixtures/rspec/before_all_fixture.rb +32 -0
  32. data/spec/integrations/fixtures/rspec/event_prof_factory_create_fixture.rb +23 -0
  33. data/spec/integrations/fixtures/rspec/event_prof_fixture.rb +51 -0
  34. data/spec/integrations/fixtures/rspec/event_prof_sidekiq_fixture.rb +54 -0
  35. data/spec/integrations/fixtures/rspec/factory_doctor_fixture.rb +33 -0
  36. data/spec/spec_helper.rb +38 -0
  37. data/spec/support/ar_models.rb +43 -0
  38. data/spec/support/instrumenter_stub.rb +19 -0
  39. data/spec/support/integration_helpers.rb +13 -0
  40. data/spec/support/transactional_context.rb +11 -0
  41. data/spec/test_prof/any_fixture_spec.rb +66 -0
  42. data/spec/test_prof/event_prof_spec.rb +138 -0
  43. data/spec/test_prof/ext/float_duration_spec.rb +12 -0
  44. data/spec/test_prof/factory_doctor_spec.rb +84 -0
  45. data/spec/test_prof/ruby_prof_spec.rb +109 -0
  46. data/spec/test_prof/stack_prof_spec.rb +73 -0
  47. data/spec/test_prof_spec.rb +23 -0
  48. data/test-prof.gemspec +35 -0
  49. metadata +34 -49
  50. data/CHANGELOG.md +0 -7
  51. data/assets/flamegraph.demo.html +0 -173
  52. data/assets/flamegraph.template.html +0 -196
  53. data/assets/src/d3-tip.js +0 -352
  54. data/assets/src/d3-tip.min.js +0 -1
  55. data/assets/src/d3.flameGraph.css +0 -92
  56. data/assets/src/d3.flameGraph.js +0 -459
  57. data/assets/src/d3.flameGraph.min.css +0 -1
  58. data/assets/src/d3.flameGraph.min.js +0 -1
  59. data/assets/src/d3.v4.min.js +0 -8
  60. data/guides/factory_default.md +0 -109
  61. data/guides/factory_prof.md +0 -85
  62. data/guides/rspec_stamp.md +0 -53
  63. data/guides/rubocop.md +0 -48
  64. data/guides/tag_prof.md +0 -52
  65. data/guides/tests_sampling.md +0 -24
  66. data/lib/test_prof/cops/rspec/aggregate_failures.rb +0 -140
  67. data/lib/test_prof/factory_default.rb +0 -58
  68. data/lib/test_prof/factory_default/factory_girl_patch.rb +0 -22
  69. data/lib/test_prof/factory_prof.rb +0 -140
  70. data/lib/test_prof/factory_prof/factory_girl_patch.rb +0 -12
  71. data/lib/test_prof/factory_prof/printers/flamegraph.rb +0 -71
  72. data/lib/test_prof/factory_prof/printers/simple.rb +0 -28
  73. data/lib/test_prof/recipes/minitest/sample.rb +0 -29
  74. data/lib/test_prof/recipes/rspec/factory_default.rb +0 -9
  75. data/lib/test_prof/recipes/rspec/sample.rb +0 -13
  76. data/lib/test_prof/rspec_stamp.rb +0 -135
  77. data/lib/test_prof/rspec_stamp/parser.rb +0 -103
  78. data/lib/test_prof/rspec_stamp/rspec.rb +0 -95
  79. data/lib/test_prof/rubocop.rb +0 -3
  80. data/lib/test_prof/tag_prof.rb +0 -8
  81. 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
@@ -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