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.
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