test-prof 0.4.9 → 0.5.0.pre1

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +63 -20
  3. data/README.md +10 -57
  4. data/assets/tagprof.demo.html +447 -0
  5. data/assets/tagprof.template.html +447 -0
  6. data/lib/minitest/event_prof_formatter.rb +18 -16
  7. data/lib/test_prof.rb +2 -1
  8. data/lib/test_prof/any_fixture.rb +80 -4
  9. data/lib/test_prof/any_fixture/dsl.rb +18 -0
  10. data/lib/test_prof/event_prof.rb +10 -108
  11. data/lib/test_prof/event_prof/custom_events.rb +30 -0
  12. data/lib/test_prof/event_prof/custom_events/factory_create.rb +1 -1
  13. data/lib/test_prof/event_prof/custom_events/sidekiq_inline.rb +1 -1
  14. data/lib/test_prof/event_prof/custom_events/sidekiq_jobs.rb +1 -1
  15. data/lib/test_prof/event_prof/minitest.rb +6 -0
  16. data/lib/test_prof/event_prof/profiler.rb +129 -0
  17. data/lib/test_prof/event_prof/rspec.rb +20 -11
  18. data/lib/test_prof/factory_all_stub.rb +32 -0
  19. data/lib/test_prof/factory_all_stub/factory_bot_patch.rb +13 -0
  20. data/lib/test_prof/factory_doctor/rspec.rb +3 -2
  21. data/lib/test_prof/factory_prof/printers/flamegraph.rb +9 -13
  22. data/lib/test_prof/recipes/active_record_shared_connection.rb +55 -0
  23. data/lib/test_prof/recipes/logging.rb +37 -0
  24. data/lib/test_prof/recipes/rspec/any_fixture.rb +4 -1
  25. data/lib/test_prof/recipes/rspec/factory_all_stub.rb +10 -0
  26. data/lib/test_prof/rspec_dissect/rspec.rb +4 -2
  27. data/lib/test_prof/rspec_stamp/rspec.rb +3 -2
  28. data/lib/test_prof/tag_prof.rb +4 -0
  29. data/lib/test_prof/tag_prof/printers/html.rb +24 -0
  30. data/lib/test_prof/tag_prof/printers/simple.rb +82 -0
  31. data/lib/test_prof/tag_prof/result.rb +38 -0
  32. data/lib/test_prof/tag_prof/rspec.rb +43 -40
  33. data/lib/test_prof/utils/html_builder.rb +21 -0
  34. data/lib/test_prof/version.rb +1 -1
  35. metadata +20 -24
  36. data/assets/logo.svg +0 -1
  37. data/assets/testprof.png +0 -0
  38. data/guides/.rubocop.yml +0 -1
  39. data/guides/any_fixture.md +0 -114
  40. data/guides/before_all.md +0 -98
  41. data/guides/event_prof.md +0 -177
  42. data/guides/factory_default.md +0 -111
  43. data/guides/factory_doctor.md +0 -119
  44. data/guides/factory_prof.md +0 -86
  45. data/guides/let_it_be.md +0 -97
  46. data/guides/rspec_dissect.md +0 -60
  47. data/guides/rspec_stamp.md +0 -52
  48. data/guides/rubocop.md +0 -48
  49. data/guides/ruby_prof.md +0 -63
  50. data/guides/stack_prof.md +0 -47
  51. data/guides/tag_prof.md +0 -51
  52. data/guides/tests_sampling.md +0 -24
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TestProf
4
+ module TagProf # :nodoc:
5
+ # Object holding all the stats for tags
6
+ class Result
7
+ attr_reader :tag, :data, :events
8
+
9
+ def initialize(tag, events = [])
10
+ @tag = tag
11
+ @events = events
12
+
13
+ @data = Hash.new do |h, k|
14
+ h[k] = { value: k, count: 0, time: 0.0 }
15
+ h[k].merge!(Hash[events.map { |event| [event, 0.0] }]) unless
16
+ events.empty?
17
+ h[k]
18
+ end
19
+ end
20
+
21
+ def track(tag, time:, events: {})
22
+ data[tag][:count] += 1
23
+ data[tag][:time] += time
24
+ events.each do |k, v|
25
+ data[tag][k] += v
26
+ end
27
+ end
28
+
29
+ def to_json
30
+ {
31
+ tag: tag,
32
+ data: data.values,
33
+ events: events
34
+ }.to_json
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,14 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "test_prof/ext/float_duration"
4
- require "test_prof/ext/string_strip_heredoc"
5
-
6
3
  module TestProf
7
4
  module TagProf
8
5
  class RSpecListener # :nodoc:
9
6
  include Logging
10
- using FloatDuration
11
- using StringStripHeredoc
12
7
 
13
8
  NOTIFICATIONS = %i[
14
9
  example_started
@@ -16,58 +11,58 @@ module TestProf
16
11
  example_passed
17
12
  ].freeze
18
13
 
14
+ attr_reader :result, :printer
15
+
19
16
  def initialize
20
- @tag = ENV['TAG_PROF'].to_sym
21
- @tags = Hash.new { |h, k| h[k] = { val: k, count: 0, time: 0.0 } }
17
+ @printer = ENV['TAG_PROF_FORMAT'] == 'html' ? Printers::HTML : Printers::Simple
18
+
19
+ @result =
20
+ if ENV['TAG_PROF_EVENT'].nil?
21
+ Result.new ENV['TAG_PROF'].to_sym
22
+ else
23
+ require "test_prof/event_prof"
24
+
25
+ @events_profiler = EventProf.build(ENV['TAG_PROF_EVENT'])
22
26
 
23
- log :info, "TagProf enabled (#{@tag})"
27
+ Result.new ENV['TAG_PROF'].to_sym, @events_profiler.events
28
+ end
29
+
30
+ log :info, "TagProf enabled (#{result.tag})"
24
31
  end
25
32
 
26
33
  def example_started(_notification)
27
34
  @ts = TestProf.now
35
+ # enable event profiling
36
+ @events_profiler.group_started(true) if @events_profiler
28
37
  end
29
38
 
30
39
  def example_finished(notification)
31
- tag = notification.example.metadata.fetch(@tag, :__unknown__)
40
+ tag = notification.example.metadata.fetch(result.tag, :__unknown__)
41
+
42
+ result.track(tag, time: TestProf.now - @ts, events: fetch_events_data)
32
43
 
33
- @tags[tag][:count] += 1
34
- @tags[tag][:time] += (TestProf.now - @ts)
44
+ # reset and disable event profilers
45
+ @events_profiler.group_started(nil) if @events_profiler
35
46
  end
36
47
 
37
48
  # NOTE: RSpec < 3.4.0 doesn't have example_finished event
38
49
  alias example_passed example_finished
39
50
  alias example_failed example_finished
40
51
 
41
- def print
42
- msgs = []
43
-
44
- msgs <<
45
- <<-MSG.strip_heredoc
46
- TagProf report for #{@tag}
47
- MSG
48
-
49
- msgs << format(
50
- "%15s %12s %6s %6s %6s %12s",
51
- @tag,
52
- 'time', 'total', '%total', '%time', 'avg'
53
- )
54
-
55
- msgs << ""
52
+ def report
53
+ printer.dump(result)
54
+ end
56
55
 
57
- total = @tags.values.inject(0) { |acc, v| acc + v[:count] }
58
- total_time = @tags.values.inject(0) { |acc, v| acc + v[:time] }
56
+ private
59
57
 
60
- @tags.values.sort_by { |v| -v[:time] }.each do |tag|
61
- msgs << format(
62
- "%15s %12s %6d %6.2f %6.2f %12s",
63
- tag[:val], tag[:time].duration, tag[:count],
64
- 100 * tag[:count].to_f / total,
65
- 100 * tag[:time] / total_time,
66
- (tag[:time] / tag[:count]).duration
67
- )
68
- end
58
+ def fetch_events_data
59
+ return {} unless @events_profiler
69
60
 
70
- log :info, msgs.join("\n")
61
+ Hash[
62
+ @events_profiler.profilers.map do |profiler|
63
+ [profiler.event, profiler.time]
64
+ end
65
+ ]
71
66
  end
72
67
  end
73
68
  end
@@ -76,14 +71,22 @@ end
76
71
  # Register TagProf listener
77
72
  TestProf.activate('TAG_PROF') do
78
73
  RSpec.configure do |config|
79
- listener = TestProf::TagProf::RSpecListener.new
74
+ listener = nil
80
75
 
81
76
  config.before(:suite) do
77
+ listener = TestProf::TagProf::RSpecListener.new
82
78
  config.reporter.register_listener(
83
79
  listener, *TestProf::TagProf::RSpecListener::NOTIFICATIONS
84
80
  )
85
81
  end
86
82
 
87
- config.after(:suite) { listener.print }
83
+ config.after(:suite) { listener.report unless listener.nil? }
88
84
  end
89
85
  end
86
+
87
+ # Activate custom events
88
+ TestProf.activate('TAG_PROF_EVENT') do
89
+ require "test_prof/event_prof"
90
+
91
+ TestProf::EventProf::CustomEvents.activate_all(ENV['TAG_PROF_EVENT'])
92
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module TestProf
6
+ module Utils
7
+ # Generates static HTML reports with injected data
8
+ module HTMLBuilder
9
+ class << self
10
+ def generate(data:, template:, output:)
11
+ template = File.read(TestProf.asset_path(template))
12
+ template.sub! '/**REPORT-DATA**/', data.to_json
13
+
14
+ outpath = TestProf.artifact_path(output)
15
+ File.write(outpath, template)
16
+ outpath
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TestProf
4
- VERSION = "0.4.9".freeze
4
+ VERSION = "0.5.0.pre1".freeze
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: test-prof
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.9
4
+ version: 0.5.0.pre1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-20 00:00:00.000000000 Z
11
+ date: 2018-05-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0.50'
75
+ version: 0.56.0
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '0.50'
82
+ version: 0.56.0
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rubocop-md
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -109,7 +109,6 @@ files:
109
109
  - README.md
110
110
  - assets/flamegraph.demo.html
111
111
  - assets/flamegraph.template.html
112
- - assets/logo.svg
113
112
  - assets/src/d3-tip.js
114
113
  - assets/src/d3-tip.min.js
115
114
  - assets/src/d3.flameGraph.css
@@ -117,28 +116,15 @@ files:
117
116
  - assets/src/d3.flameGraph.min.css
118
117
  - assets/src/d3.flameGraph.min.js
119
118
  - assets/src/d3.v4.min.js
120
- - assets/testprof.png
121
- - guides/.rubocop.yml
122
- - guides/any_fixture.md
123
- - guides/before_all.md
124
- - guides/event_prof.md
125
- - guides/factory_default.md
126
- - guides/factory_doctor.md
127
- - guides/factory_prof.md
128
- - guides/let_it_be.md
129
- - guides/rspec_dissect.md
130
- - guides/rspec_stamp.md
131
- - guides/rubocop.md
132
- - guides/ruby_prof.md
133
- - guides/stack_prof.md
134
- - guides/tag_prof.md
135
- - guides/tests_sampling.md
119
+ - assets/tagprof.demo.html
120
+ - assets/tagprof.template.html
136
121
  - lib/minitest/base_reporter.rb
137
122
  - lib/minitest/event_prof_formatter.rb
138
123
  - lib/minitest/test_prof_plugin.rb
139
124
  - lib/test-prof.rb
140
125
  - lib/test_prof.rb
141
126
  - lib/test_prof/any_fixture.rb
127
+ - lib/test_prof/any_fixture/dsl.rb
142
128
  - lib/test_prof/cops/rspec/aggregate_failures.rb
143
129
  - lib/test_prof/event_prof.rb
144
130
  - lib/test_prof/event_prof/custom_events.rb
@@ -147,12 +133,15 @@ files:
147
133
  - lib/test_prof/event_prof/custom_events/sidekiq_jobs.rb
148
134
  - lib/test_prof/event_prof/instrumentations/active_support.rb
149
135
  - lib/test_prof/event_prof/minitest.rb
136
+ - lib/test_prof/event_prof/profiler.rb
150
137
  - lib/test_prof/event_prof/rspec.rb
151
138
  - lib/test_prof/ext/active_record_3.rb
152
139
  - lib/test_prof/ext/array_bsearch_index.rb
153
140
  - lib/test_prof/ext/float_duration.rb
154
141
  - lib/test_prof/ext/string_strip_heredoc.rb
155
142
  - lib/test_prof/ext/string_truncate.rb
143
+ - lib/test_prof/factory_all_stub.rb
144
+ - lib/test_prof/factory_all_stub/factory_bot_patch.rb
156
145
  - lib/test_prof/factory_bot.rb
157
146
  - lib/test_prof/factory_default.rb
158
147
  - lib/test_prof/factory_default/factory_bot_patch.rb
@@ -168,9 +157,12 @@ files:
168
157
  - lib/test_prof/factory_prof/printers/flamegraph.rb
169
158
  - lib/test_prof/factory_prof/printers/simple.rb
170
159
  - lib/test_prof/logging.rb
160
+ - lib/test_prof/recipes/active_record_shared_connection.rb
161
+ - lib/test_prof/recipes/logging.rb
171
162
  - lib/test_prof/recipes/minitest/sample.rb
172
163
  - lib/test_prof/recipes/rspec/any_fixture.rb
173
164
  - lib/test_prof/recipes/rspec/before_all.rb
165
+ - lib/test_prof/recipes/rspec/factory_all_stub.rb
174
166
  - lib/test_prof/recipes/rspec/factory_default.rb
175
167
  - lib/test_prof/recipes/rspec/let_it_be.rb
176
168
  - lib/test_prof/recipes/rspec/sample.rb
@@ -185,8 +177,12 @@ files:
185
177
  - lib/test_prof/stack_prof.rb
186
178
  - lib/test_prof/stack_prof/rspec.rb
187
179
  - lib/test_prof/tag_prof.rb
180
+ - lib/test_prof/tag_prof/printers/html.rb
181
+ - lib/test_prof/tag_prof/printers/simple.rb
182
+ - lib/test_prof/tag_prof/result.rb
188
183
  - lib/test_prof/tag_prof/rspec.rb
189
184
  - lib/test_prof/utils.rb
185
+ - lib/test_prof/utils/html_builder.rb
190
186
  - lib/test_prof/utils/sized_ordered_set.rb
191
187
  - lib/test_prof/version.rb
192
188
  homepage: http://github.com/palkan/test-prof
@@ -204,12 +200,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
204
200
  version: 2.2.0
205
201
  required_rubygems_version: !ruby/object:Gem::Requirement
206
202
  requirements:
207
- - - ">="
203
+ - - ">"
208
204
  - !ruby/object:Gem::Version
209
- version: '0'
205
+ version: 1.3.1
210
206
  requirements: []
211
207
  rubyforge_project:
212
- rubygems_version: 2.7.4
208
+ rubygems_version: 2.7.6
213
209
  signing_key:
214
210
  specification_version: 4
215
211
  summary: Ruby applications tests profiling tools
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 86 100"><defs><linearGradient id="a" x1="43" y1="100" x2="43" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#b5afbf"/><stop offset="1" stop-color="#dbd1d6"/></linearGradient><linearGradient id="b" x1="16" y1="87" x2="16" y2="73" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#a99ea7"/><stop offset=".6" stop-color="#c8b8c1"/></linearGradient><linearGradient id="c" x1="14" y1="75" x2="14" y2="79" xlink:href="#linear-gradient-2"/><linearGradient id="d" x1="34" x2="34" xlink:href="#linear-gradient-2"/><linearGradient id="e" x1="36" y1="79" x2="36" y2="83" xlink:href="#linear-gradient-2"/></defs><rect fill="url(#a)" width="86" height="100" rx="13" ry="13"/><rect fill="#fef7f9" x="1" y="1" width="84" height="94" rx="12" ry="12"/><path fill="#fff" d="M73 1H13A12 12 0 0 0 1 13v1A12 12 0 0 1 13 2h60a12 12 0 0 1 12 12v-1A12 12 0 0 0 73 1z"/><rect fill="#7e628f" x="9" y="9" width="68" height="58" rx="4" ry="4"/><path fill="#675077" d="M73 9H13a4 4 0 0 0-4 4v1a4 4 0 0 1 4-4h60a4 4 0 0 1 4 4v-1a4 4 0 0 0-4-4z"/><rect fill="#d84b9d" x="51" y="73" width="26" height="14" rx="4" ry="4"/><rect fill="#f159b0" x="51" y="73" width="26" height="12" rx="4" ry="4"/><path fill="#fc7acb" d="M73 84H55a4 4 0 0 1-4-4v1a4 4 0 0 0 4 4h18a4 4 0 0 0 4-4v-1a4 4 0 0 1-4 4z"/><circle fill="url(#b)" cx="16" cy="80" r="7"/><circle fill="#dec7d4" cx="16" cy="79" r="6"/><path fill="#f9edf4" d="M16 74a6 6 0 0 1 6 5.5V79a6 6 0 0 0-12 0v.5a6 6 0 0 1 6-5.5z"/><circle fill="url(#c)" cx="14" cy="77" r="2"/><circle fill="url(#d)" cx="34" cy="80" r="7"/><circle fill="#dec7d4" cx="34" cy="79" r="6"/><path fill="#f9edf4" d="M34 74a6 6 0 0 1 6 5.5V79a6 6 0 0 0-12 0v.5a6 6 0 0 1 6-5.5z"/><circle fill="url(#e)" cx="36" cy="81" r="2"/><path fill="#9072a0" d="M27 60.8A11.8 11.8 0 1 1 38.8 49 11.8 11.8 0 0 1 27 60.8zm0-20a8.2 8.2 0 1 0 8.2 8.2 8.2 8.2 0 0 0-8.2-8.2z"/><path fill="#fe5bb7" d="M37 51a2 2 0 0 1-2-2 8 8 0 0 0-8-8 2 2 0 0 1 0-4 12 12 0 0 1 12 12 2 2 0 0 1-2 2z"/><path fill="#9072a0" d="M59 60.8A11.8 11.8 0 1 1 70.8 49 11.8 11.8 0 0 1 59 60.8zm0-20a8.2 8.2 0 1 0 8.2 8.2 8.2 8.2 0 0 0-8.2-8.2z"/><path fill="#5bd3e8" d="M59 61a12 12 0 0 1-12-12 2 2 0 0 1 4 0 8 8 0 1 0 8-8 2 2 0 0 1 0-4 12 12 0 0 1 0 24z"/><path fill="#ffae43" d="M70 17.5l-11 11-4-5-7.8 8.8-11-21.2L26 25.7l-4-4-8 10.7-5-8V28l5 7.8 8-11.3 4 4L36 15l10.8 20.8 8.2-9.2 4 5 11-11 7 8.5v-3l-7-8.4z"/></svg>
Binary file
@@ -1 +0,0 @@
1
- # Leave it blank to use default Rubocop config in guides
@@ -1,114 +0,0 @@
1
- # Any Fixture
2
-
3
- Fixtures are the great way to increase your test suite performance, but for the large project, they are very hard to maintain.
4
-
5
- We propose a more general approach to lazy-generate the _global_ state for your test suite – AnyFixture.
6
-
7
- With AnyFixture you can use any block of code for data generation, and it will take care of cleaning it out at the end of the run.
8
-
9
- Consider an example:
10
-
11
- ```ruby
12
- # The best way to use AnyFixture is through RSpec shared contexts
13
- RSpec.shared_context 'account', account: true do
14
- # You should call AnyFixture outside of transaction to re-use the same
15
- # data between examples
16
- before(:all) do
17
- # The provided name ("account") should be unique.
18
- @account = TestProf::AnyFixture.register(:account) do
19
- # Do anything here, AnyFixture keeps track of affected DB tables
20
- # For example, you can use factories here
21
- FactoryGirl.create(:account)
22
-
23
- # or with Fabrication
24
- Fabricate(:account)
25
-
26
- # or with plain old AR
27
- Account.create!(name: 'test')
28
- end
29
- end
30
-
31
- let(:account) { @account }
32
-
33
- # Or hard-reload object if there is chance of in-place modification
34
- let(:account) { Account.find(@account.id) }
35
- end
36
-
37
- # Then in your tests
38
-
39
- # Active this fixture using a tag
40
- describe UsersController, :account do
41
- # ...
42
- end
43
-
44
- # This test also uses the same account record,
45
- # no double-creation
46
- describe PostsController, :account do
47
- # ...
48
- end
49
- ```
50
-
51
- ## Instructions
52
-
53
- In your `spec_helper.rb`:
54
-
55
- ```ruby
56
- require 'test_prof/recipes/rspec/any_fixture'
57
- ```
58
-
59
- Now you can use `TestProf::AnyFixture` in your tests.
60
-
61
- ## Caveats
62
-
63
- `AnyFixture` cleans tables in the reverse order as compared to the order they were populated. That
64
- means when you register a fixture which references a not-yet-registered table, a
65
- foreign-key violation error *might* occur (if any). An example is worth more than 1000
66
- words:
67
-
68
- ```ruby
69
- class Author < ApplicationRecord
70
- has_many :articles
71
- end
72
-
73
- class Article < ApplicationRecord
74
- belongs_to :author
75
- end
76
- ```
77
-
78
- And the shared contexts:
79
-
80
- ```ruby
81
- RSpec.shared_context 'author' do
82
- before(:all) do
83
- @author = TestProf::AnyFixture.register(:author) do
84
- FactoryGirl.create(:account)
85
- end
86
- end
87
-
88
- let(:author) { @author }
89
- end
90
-
91
- RSpec.shared_context 'article' do
92
- before(:all) do
93
- # outside of AnyFixture, we don't know about its dependent tables
94
- author = FactoryGirl.create(:author)
95
-
96
- @article = TestProf::AnyFixture.register(:article) do
97
- FactoryGirl.create(:article, author: author)
98
- end
99
- end
100
-
101
- let(:article) { @article }
102
- end
103
- ```
104
-
105
- Then in some example:
106
-
107
- ```ruby
108
- # This one adds only the 'articles' table to the list of affected tables
109
- include_context 'article'
110
- # And this one adds the 'authors' table
111
- include_context 'author'
112
- ```
113
-
114
- Now we have the following affected tables list: `['articles', 'authors']`. At the end of the suite, the 'authors' table is cleaned first which leads to a foreign-key violation error.