test-prof 0.4.9 → 0.5.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
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.