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