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
@@ -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.before(:suite) do
87
- config.reporter.register_listener(
88
- listener, *TestProf::FactoryDoctor::RSpecListener::NOTIFICATIONS
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
@@ -48,8 +48,8 @@ module TestProf
48
48
  :include_threads, :eliminate_methods
49
49
 
50
50
  def initialize
51
- @printer = ENV.fetch('TEST_RUBY_PROF_PRINTER', :call_stack).to_sym
52
- @mode = ENV.fetch('TEST_RUBY_PROF_MODE', :wall).to_sym
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
- return ['custom', printer] if printer.is_a?(Module)
70
+ type = ENV['TEST_RUBY_PROF_PRINTER'] || printer
70
71
 
71
- type = printer.to_s
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.artefact_path(
111
- "ruby-prof-report-#{printer}-#{config.mode}-#{name}.html"
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
 
@@ -25,8 +25,8 @@ module TestProf
25
25
  attr_accessor :mode, :interval, :raw
26
26
 
27
27
  def initialize
28
- @mode = ENV.fetch('TEST_STACK_PROF_MODE', :wall).to_sym
29
- @raw = ENV['TEST_STACK_PROF_RAW'] == '1'
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.artefact_path(
102
- "stack-prof-report-#{config.mode}#{config.raw ? '-raw' : ''}-#{name}.dump"
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
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TestProf
4
- VERSION = "0.1.0.beta4"
4
+ VERSION = "0.1.0.pre2"
5
5
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe "AnyFixture" do
6
+ specify "it works" do
7
+ output = run_rspec('any_fixture')
8
+
9
+ expect(output).to include("3 examples, 0 failures")
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe "BeforeAll" do
6
+ specify "it works" do
7
+ output = run_rspec('before_all')
8
+
9
+ expect(output).to include("3 examples, 0 failures")
10
+ end
11
+ end
@@ -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