test-prof 0.3.0.pre2 → 0.3.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +35 -1
- data/README.md +8 -2
- data/guides/event_prof.md +10 -0
- data/guides/factory_doctor.md +10 -0
- data/guides/rspec_dissect.md +13 -4
- data/guides/ruby_prof.md +4 -4
- data/guides/stack_prof.md +1 -1
- data/lib/test_prof/cops/rspec/aggregate_failures.rb +2 -2
- data/lib/test_prof/event_prof.rb +15 -4
- data/lib/test_prof/event_prof/custom_events/factory_create.rb +5 -1
- data/lib/test_prof/event_prof/custom_events/sidekiq_inline.rb +5 -1
- data/lib/test_prof/event_prof/custom_events/sidekiq_jobs.rb +5 -1
- data/lib/test_prof/event_prof/rspec.rb +40 -3
- data/lib/test_prof/ext/array_bsearch_index.rb +15 -0
- data/lib/test_prof/ext/string_strip_heredoc.rb +15 -0
- data/lib/test_prof/factory_doctor.rb +10 -2
- data/lib/test_prof/factory_doctor/rspec.rb +38 -2
- data/lib/test_prof/factory_prof/printers/simple.rb +4 -1
- data/lib/test_prof/recipes/rspec/any_fixture.rb +1 -1
- data/lib/test_prof/recipes/rspec/let_it_be.rb +8 -2
- data/lib/test_prof/rspec_dissect.rb +10 -2
- data/lib/test_prof/rspec_dissect/rspec.rb +42 -6
- data/lib/test_prof/rspec_stamp.rb +51 -6
- data/lib/test_prof/rspec_stamp/parser.rb +55 -9
- data/lib/test_prof/rspec_stamp/rspec.rb +10 -34
- data/lib/test_prof/ruby_prof.rb +7 -3
- data/lib/test_prof/stack_prof.rb +8 -5
- data/lib/test_prof/tag_prof/rspec.rb +3 -1
- data/lib/test_prof/utils/sized_ordered_set.rb +6 -1
- data/lib/test_prof/version.rb +1 -1
- metadata +9 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb103d86ccc7a513de8b30ae8ddf536c9bbc6255
|
4
|
+
data.tar.gz: f0fe1c206faab5761b4419ccbf8745f4060d3e82
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f6a4881cfc954438fa4184c2cd9be1784d10bfcd6d92931ea5132c8422aa08866b77e60ec91066ddd6afb620c524e99b4584818d158f396176f9649ef3a0e038
|
7
|
+
data.tar.gz: a699bd6e308b370aa4e53447025551a5b2f30249eb893356d30238eaed4dc135bb69335c780ea9d0b306ce569bca29117494ab91b6a70bbb7a22f714a65f827f
|
data/CHANGELOG.md
CHANGED
@@ -2,7 +2,21 @@
|
|
2
2
|
|
3
3
|
## 0.3.0
|
4
4
|
|
5
|
-
Features:
|
5
|
+
### Features:
|
6
|
+
|
7
|
+
- Combine RSpecStamp with FactoryDoctor. ([@palkan][])
|
8
|
+
|
9
|
+
Automatically mark _bad_ examples with custom tags.
|
10
|
+
|
11
|
+
- [#17](https://github.com/palkan/test-prof/pull/17) Combine RSpecStamp with EventProf and RSpecDissect. ([@palkan][])
|
12
|
+
|
13
|
+
It is possible now to automatically mark _slow_ examples and groups with custom tags. For example:
|
14
|
+
|
15
|
+
```sh
|
16
|
+
EVENT_PROF="sql.active_record" EVENT_PROF_STAMP="slow:sql" rspec ...
|
17
|
+
```
|
18
|
+
|
19
|
+
After running the command above the top 5 slowest example groups would be marked with `slow: :sql` tag.
|
6
20
|
|
7
21
|
- [#14](https://github.com/palkan/test-prof/pull/14) RSpecDissect profiler. ([@palkan][])
|
8
22
|
|
@@ -13,6 +27,26 @@ and memoization helpers (i.e. `let`) in your tests.
|
|
13
27
|
|
14
28
|
Just like `let`, but persist the result for the whole group (i.e. `let` + `before_all`).
|
15
29
|
|
30
|
+
### Improvements:
|
31
|
+
|
32
|
+
- Add ability to specify RubyProf report through `TEST_RUBY_PROF` env variable. ([@palkan][])
|
33
|
+
|
34
|
+
- Add ability to specify StackProf raw mode through `TEST_STACK_PROF` env variable. ([@palkan][])
|
35
|
+
|
36
|
+
### Changes
|
37
|
+
|
38
|
+
- Use RubyProf `FlatPrinter` by default (was `CallStackPrinter`). ([@palkan][])
|
39
|
+
|
40
|
+
## 0.2.5
|
41
|
+
|
42
|
+
- [#16](https://github.com/palkan/test-prof/pull/16) Support Ruby >= 2.2.0 (was >= 2.3.0). ([@palkan][])
|
43
|
+
|
44
|
+
## 0.2.4
|
45
|
+
|
46
|
+
- EventProf: Fix regression bug with examples profiling. ([@palkan][])
|
47
|
+
|
48
|
+
There was a bug when an event occurs before the example has started (e.g. in `before(:context)` hook).
|
49
|
+
|
16
50
|
## 0.2.3
|
17
51
|
|
18
52
|
- Minor improvements. ([@palkan][])
|
data/README.md
CHANGED
@@ -10,7 +10,7 @@ Simply speaking, slow tests waste your time making you less productive.
|
|
10
10
|
|
11
11
|
TestProf toolbox aims to help you identify bottlenecks in your test suite. It contains:
|
12
12
|
|
13
|
-
- Plug'n'Play integrations for general Ruby profilers ([`ruby-prof`](https://github.com/ruby-prof), [`stackprof`](https://github.com/tmm1/stackprof))
|
13
|
+
- Plug'n'Play integrations for general Ruby profilers ([`ruby-prof`](https://github.com/ruby-prof/ruby-prof), [`stackprof`](https://github.com/tmm1/stackprof))
|
14
14
|
|
15
15
|
- Factories usage analyzers and profilers
|
16
16
|
|
@@ -20,10 +20,16 @@ TestProf toolbox aims to help you identify bottlenecks in your test suite. It co
|
|
20
20
|
|
21
21
|
- etc.
|
22
22
|
|
23
|
-
Of course, we have some [solutions](#tips-and-tricks) for common performance issues too, bundled into the gem.
|
23
|
+
Of course, we have some [solutions](#tips-and-tricks-or-recipes) for common performance issues too, bundled into the gem.
|
24
24
|
|
25
25
|
See [Table of Contents](#table-of-contents) for more.
|
26
26
|
|
27
|
+
Supported Ruby versions:
|
28
|
+
|
29
|
+
- Ruby (MRI) >= 2.2.0
|
30
|
+
|
31
|
+
- JRuby >= 9.1.0.0
|
32
|
+
|
27
33
|
<a href="https://evilmartians.com/">
|
28
34
|
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
|
29
35
|
|
data/guides/event_prof.md
CHANGED
@@ -66,6 +66,16 @@ EVENT_PROF_RANK=count EVENT_PROF='instantiation.active_record' be rspec
|
|
66
66
|
|
67
67
|
See [event_prof.rb](https://github.com/palkan/test-prof/tree/master/lib/test_prof/event_prof.rb) for all available configuration options and their usage.
|
68
68
|
|
69
|
+
## Using with RSpecStamp
|
70
|
+
|
71
|
+
EventProf can be used with [RSpec Stamp](https://github.com/palkan/test-prof/tree/master/guides/rspec_stamp.md) to automatically mark _slow_ examples with custom tags. For example:
|
72
|
+
|
73
|
+
```sh
|
74
|
+
EVENT_PROF="sql.active_record" EVENT_PROF_STAMP="slow:sql" rspec ...
|
75
|
+
```
|
76
|
+
|
77
|
+
After running the command above the slowest example groups (and examples if configured) would be marked with the `slow: :sql` tag.
|
78
|
+
|
69
79
|
## Custom Instrumentation
|
70
80
|
|
71
81
|
To use EventProf with your instrumentation engine just complete the two following steps:
|
data/guides/factory_doctor.md
CHANGED
@@ -62,3 +62,13 @@ To activate FactoryDoctor use `FDOC` environment variable:
|
|
62
62
|
```sh
|
63
63
|
FDOC=1 rspec ...
|
64
64
|
```
|
65
|
+
|
66
|
+
## Using with RSpecStamp
|
67
|
+
|
68
|
+
FactoryDoctor can be used with [RSpec Stamp](https://github.com/palkan/test-prof/tree/master/guides/rspec_stamp.md) to automatically mark _bad_ examples with custom tags. For example:
|
69
|
+
|
70
|
+
```sh
|
71
|
+
FDOC=1 FDOC_STAMP="fdoc:consider" rspec ...
|
72
|
+
```
|
73
|
+
|
74
|
+
After running the command above all _potentially_ bad examples would be marked with the `fdoc: :consider` tag.
|
data/guides/rspec_dissect.md
CHANGED
@@ -35,15 +35,24 @@ AvailableSlotsController (./spec/controllers/available_slots_controller_spec.rb:
|
|
35
35
|
|
36
36
|
RSpecDissect can only be used with RSpec (which is clear from the name).
|
37
37
|
|
38
|
-
To activate RSpecDissect use `
|
38
|
+
To activate RSpecDissect use `RD_PROF` environment variable:
|
39
39
|
|
40
40
|
```sh
|
41
|
-
|
41
|
+
RD_PROF=1 rspec ...
|
42
42
|
```
|
43
43
|
|
44
|
-
You can also specify the number of top slow groups through `
|
44
|
+
You can also specify the number of top slow groups through `RD_PROF_TOP` variable:
|
45
45
|
|
46
46
|
```sh
|
47
|
-
|
47
|
+
RD_PROF=1 RD_PROF_TOP=10 rspec ...
|
48
48
|
```
|
49
49
|
|
50
|
+
## Using with RSpecStamp
|
51
|
+
|
52
|
+
RSpecDissect can be used with [RSpec Stamp](https://github.com/palkan/test-prof/tree/master/guides/rspec_stamp.md) to automatically mark _slow_ examples with custom tags. For example:
|
53
|
+
|
54
|
+
```sh
|
55
|
+
RD_PROF=1 RD_PROF_STAMP="slow" rspec ...
|
56
|
+
```
|
57
|
+
|
58
|
+
After running the command above the slowest example groups would be marked with the `:slow` tag.
|
data/guides/ruby_prof.md
CHANGED
@@ -42,21 +42,21 @@ end
|
|
42
42
|
|
43
43
|
The most useful configuration option is `printer` – it allows you to specify a RubyProf [printer](https://github.com/ruby-prof/ruby-prof#printers).
|
44
44
|
|
45
|
-
You can specify a printer through environment variable `
|
45
|
+
You can specify a printer through environment variable `TEST_RUBY_PROF`:
|
46
46
|
|
47
47
|
```sh
|
48
|
-
|
48
|
+
TEST_RUBY_PROF=call_stack bundle exec rake test
|
49
49
|
```
|
50
50
|
|
51
51
|
Or in your code:
|
52
52
|
|
53
53
|
```ruby
|
54
54
|
TestProf::RubyProf.configure do |config|
|
55
|
-
config.printer = :
|
55
|
+
config.printer = :call_stack
|
56
56
|
end
|
57
57
|
```
|
58
58
|
|
59
|
-
By default, we use `
|
59
|
+
By default, we use `FlatPrinter`.
|
60
60
|
|
61
61
|
Also, you can specify RubyProf mode (`wall`, `cpu`, etc) through `TEST_RUBY_PROF_MODE` env variable.
|
62
62
|
|
data/guides/stack_prof.md
CHANGED
@@ -42,6 +42,6 @@ end
|
|
42
42
|
|
43
43
|
You can change StackProf mode (which is `wall` by default) through `TEST_STACK_PROF_MODE` env variable.
|
44
44
|
|
45
|
-
If you want to generate flame graphs you should collect _raw_ data. Turn _raw_ collection on by passing `
|
45
|
+
If you want to generate flame graphs you should collect _raw_ data. Turn _raw_ collection on by passing `TEST_STACK_PROF=raw`.
|
46
46
|
|
47
47
|
See [stack_prof.rb](https://github.com/palkan/test-prof/tree/master/lib/test_prof/stack_prof.rb) for all available configuration options and their usage.
|
@@ -39,7 +39,7 @@ module RuboCop
|
|
39
39
|
|
40
40
|
def on_block(node)
|
41
41
|
method, _args, body = *node
|
42
|
-
return unless body
|
42
|
+
return unless body && body.begin_type?
|
43
43
|
|
44
44
|
_receiver, method_name, _object = *method
|
45
45
|
return unless GROUP_BLOCKS.include?(method_name)
|
@@ -109,7 +109,7 @@ module RuboCop
|
|
109
109
|
end
|
110
110
|
|
111
111
|
def oneliner?(node)
|
112
|
-
node
|
112
|
+
node && node.block_type? &&
|
113
113
|
(node.source.lines.size == 1) &&
|
114
114
|
example_node?(node)
|
115
115
|
end
|
data/lib/test_prof/event_prof.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "test_prof/rspec_stamp"
|
3
4
|
require "test_prof/event_prof/instrumentations/active_support"
|
4
5
|
require "test_prof/utils/sized_ordered_set"
|
5
6
|
|
@@ -39,6 +40,13 @@ module TestProf
|
|
39
40
|
@top_count = (ENV['EVENT_PROF_TOP'] || 5).to_i
|
40
41
|
@per_example = ENV['EVENT_PROF_EXAMPLES'] == '1'
|
41
42
|
@rank_by = (ENV['EVENT_PROF_RANK'] || :time).to_sym
|
43
|
+
@stamp = ENV['EVENT_PROF_STAMP']
|
44
|
+
|
45
|
+
RSpecStamp.config.tags = @stamp if stamp?
|
46
|
+
end
|
47
|
+
|
48
|
+
def stamp?
|
49
|
+
!@stamp.nil?
|
42
50
|
end
|
43
51
|
|
44
52
|
def per_example?
|
@@ -76,7 +84,7 @@ module TestProf
|
|
76
84
|
class Profiler # :nodoc:
|
77
85
|
include TestProf::Logging
|
78
86
|
|
79
|
-
attr_reader :event, :
|
87
|
+
attr_reader :event, :total_count, :total_time
|
80
88
|
|
81
89
|
def initialize(event:, instrumenter:)
|
82
90
|
@event = event
|
@@ -105,7 +113,7 @@ module TestProf
|
|
105
113
|
@time += time
|
106
114
|
@count += 1
|
107
115
|
|
108
|
-
return
|
116
|
+
return if @current_example.nil?
|
109
117
|
|
110
118
|
@example_time += time
|
111
119
|
@example_count += 1
|
@@ -124,8 +132,10 @@ module TestProf
|
|
124
132
|
@current_group = nil
|
125
133
|
end
|
126
134
|
|
127
|
-
def example_started(
|
128
|
-
|
135
|
+
def example_started(id)
|
136
|
+
return unless config.per_example?
|
137
|
+
reset_example!
|
138
|
+
@current_example = id
|
129
139
|
end
|
130
140
|
|
131
141
|
def example_finished(id)
|
@@ -134,6 +144,7 @@ module TestProf
|
|
134
144
|
|
135
145
|
data = { id: id, time: @example_time, count: @example_count }
|
136
146
|
@examples << data unless data[rank_by].zero?
|
147
|
+
@current_example = nil
|
137
148
|
end
|
138
149
|
|
139
150
|
def results
|
@@ -1,5 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "test_prof/ext/string_strip_heredoc"
|
4
|
+
|
5
|
+
using TestProf::StringStripHeredoc
|
6
|
+
|
3
7
|
module TestProf::EventProf::CustomEvents
|
4
8
|
module FactoryCreate # :nodoc: all
|
5
9
|
module RunnerPatch
|
@@ -42,7 +46,7 @@ end
|
|
42
46
|
TestProf.activate('EVENT_PROF', 'factory.create') do
|
43
47
|
if TestProf.require(
|
44
48
|
'factory_girl',
|
45
|
-
|
49
|
+
<<-MSG.strip_heredoc
|
46
50
|
Failed to load FactoryGirl.
|
47
51
|
|
48
52
|
Make sure that "factory_girl" gem is in your Gemfile.
|
@@ -1,5 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "test_prof/ext/string_strip_heredoc"
|
4
|
+
|
5
|
+
using TestProf::StringStripHeredoc
|
6
|
+
|
3
7
|
module TestProf::EventProf::CustomEvents
|
4
8
|
module SidekiqInline # :nodoc: all
|
5
9
|
module ClientPatch
|
@@ -39,7 +43,7 @@ end
|
|
39
43
|
TestProf.activate('EVENT_PROF', 'sidekiq.inline') do
|
40
44
|
if TestProf.require(
|
41
45
|
'sidekiq/testing',
|
42
|
-
|
46
|
+
<<-MSG.strip_heredoc
|
43
47
|
Failed to load Sidekiq.
|
44
48
|
|
45
49
|
Make sure that "sidekiq" gem is in your Gemfile.
|
@@ -1,5 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "test_prof/ext/string_strip_heredoc"
|
4
|
+
|
5
|
+
using TestProf::StringStripHeredoc
|
6
|
+
|
3
7
|
module TestProf::EventProf::CustomEvents
|
4
8
|
module SidekiqJobs # :nodoc: all
|
5
9
|
module ClientPatch
|
@@ -26,7 +30,7 @@ end
|
|
26
30
|
TestProf.activate('EVENT_PROF', 'sidekiq.jobs') do
|
27
31
|
if TestProf.require(
|
28
32
|
'sidekiq/testing',
|
29
|
-
|
33
|
+
<<-MSG.strip_heredoc
|
30
34
|
Failed to load Sidekiq.
|
31
35
|
|
32
36
|
Make sure that "sidekiq" gem is in your Gemfile.
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "test_prof/ext/float_duration"
|
4
4
|
require "test_prof/ext/string_truncate"
|
5
|
+
require "test_prof/ext/string_strip_heredoc"
|
5
6
|
|
6
7
|
module TestProf
|
7
8
|
module EventProf
|
@@ -9,6 +10,7 @@ module TestProf
|
|
9
10
|
include Logging
|
10
11
|
using FloatDuration
|
11
12
|
using StringTruncate
|
13
|
+
using StringStripHeredoc
|
12
14
|
|
13
15
|
NOTIFICATIONS = %i[
|
14
16
|
example_group_started
|
@@ -45,7 +47,7 @@ module TestProf
|
|
45
47
|
msgs = []
|
46
48
|
|
47
49
|
msgs <<
|
48
|
-
|
50
|
+
<<-MSG.strip_heredoc
|
49
51
|
EventProf results for #{@profiler.event}
|
50
52
|
|
51
53
|
Total time: #{@profiler.total_time.duration}
|
@@ -60,7 +62,7 @@ module TestProf
|
|
60
62
|
location = group[:id].metadata[:location]
|
61
63
|
|
62
64
|
msgs <<
|
63
|
-
|
65
|
+
<<-GROUP.strip_heredoc
|
64
66
|
#{description.truncate} (#{location}) – #{group[:time].duration} (#{group[:count]} / #{group[:examples]})
|
65
67
|
GROUP
|
66
68
|
end
|
@@ -72,13 +74,48 @@ module TestProf
|
|
72
74
|
description = example[:id].description
|
73
75
|
location = example[:id].metadata[:location]
|
74
76
|
msgs <<
|
75
|
-
|
77
|
+
<<-GROUP.strip_heredoc
|
76
78
|
#{description.truncate} (#{location}) – #{example[:time].duration} (#{example[:count]})
|
77
79
|
GROUP
|
78
80
|
end
|
79
81
|
end
|
80
82
|
|
81
83
|
log :info, msgs.join
|
84
|
+
|
85
|
+
stamp! if EventProf.config.stamp?
|
86
|
+
end
|
87
|
+
|
88
|
+
def stamp!
|
89
|
+
result = @profiler.results
|
90
|
+
|
91
|
+
stamper = RSpecStamp::Stamper.new
|
92
|
+
|
93
|
+
examples = Hash.new { |h, k| h[k] = [] }
|
94
|
+
|
95
|
+
(result[:groups].to_a + result.fetch(:examples, []).to_a)
|
96
|
+
.map { |obj| obj[:id].metadata[:location] }.each do |location|
|
97
|
+
file, line = location.split(":")
|
98
|
+
examples[file] << line.to_i
|
99
|
+
end
|
100
|
+
|
101
|
+
examples.each do |file, lines|
|
102
|
+
stamper.stamp_file(file, lines.uniq)
|
103
|
+
end
|
104
|
+
|
105
|
+
msgs = []
|
106
|
+
|
107
|
+
msgs <<
|
108
|
+
<<-MSG.strip_heredoc
|
109
|
+
RSpec Stamp results
|
110
|
+
|
111
|
+
Total patches: #{stamper.total}
|
112
|
+
Total files: #{examples.keys.size}
|
113
|
+
|
114
|
+
Failed patches: #{stamper.failed}
|
115
|
+
Ignored files: #{stamper.ignored}
|
116
|
+
MSG
|
117
|
+
|
118
|
+
log :info, msgs.join
|
82
119
|
end
|
83
120
|
end
|
84
121
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TestProf
|
4
|
+
# Ruby 2.3 #bsearch_index method (for usage with older Rubies)
|
5
|
+
# Straighforward and non-optimal implementation,
|
6
|
+
# just for compatiblity
|
7
|
+
module ArrayBSearchIndex
|
8
|
+
refine Array do
|
9
|
+
def bsearch_index(&block)
|
10
|
+
el = bsearch(&block)
|
11
|
+
index(el)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TestProf
|
4
|
+
# Add #strip_heredoc method to use instead of
|
5
|
+
# squiggly docs (to support older Rubies)
|
6
|
+
module StringStripHeredoc
|
7
|
+
refine String do
|
8
|
+
def strip_heredoc
|
9
|
+
min = scan(/^[ \t]*(?=\S)/).min
|
10
|
+
indent = min ? min.size : 0
|
11
|
+
gsub(/^[ \t]{#{indent}}/, '')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -16,7 +16,7 @@ module TestProf
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def bad?
|
19
|
-
count
|
19
|
+
count > 0 && queries_count.zero?
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
@@ -54,6 +54,14 @@ module TestProf
|
|
54
54
|
defined?(::FactoryGirl)
|
55
55
|
|
56
56
|
subscribe!
|
57
|
+
|
58
|
+
@stamp = ENV['FDOC_STAMP']
|
59
|
+
|
60
|
+
RSpecStamp.config.tags = @stamp if stamp?
|
61
|
+
end
|
62
|
+
|
63
|
+
def stamp?
|
64
|
+
!@stamp.nil?
|
57
65
|
end
|
58
66
|
|
59
67
|
def start
|
@@ -111,7 +119,7 @@ module TestProf
|
|
111
119
|
end
|
112
120
|
|
113
121
|
def within_factory?
|
114
|
-
@depth
|
122
|
+
@depth > 0
|
115
123
|
end
|
116
124
|
|
117
125
|
def ignore?
|
@@ -1,14 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "test_prof/ext/float_duration"
|
4
|
+
require "test_prof/ext/string_strip_heredoc"
|
4
5
|
|
5
6
|
module TestProf
|
6
7
|
module FactoryDoctor
|
7
8
|
class RSpecListener # :nodoc:
|
8
9
|
include Logging
|
9
10
|
using FloatDuration
|
11
|
+
using StringStripHeredoc
|
10
12
|
|
11
|
-
SUCCESS_MESSAGE = 'FactoryDoctor says: "Looks good to me!"'
|
13
|
+
SUCCESS_MESSAGE = 'FactoryDoctor says: "Looks good to me!"'.freeze
|
12
14
|
|
13
15
|
NOTIFICATIONS = %i[
|
14
16
|
example_started
|
@@ -49,7 +51,7 @@ module TestProf
|
|
49
51
|
msgs = []
|
50
52
|
|
51
53
|
msgs <<
|
52
|
-
|
54
|
+
<<-MSG.strip_heredoc
|
53
55
|
FactoryDoctor report
|
54
56
|
|
55
57
|
Total (potentially) bad examples: #{@count}
|
@@ -68,6 +70,40 @@ module TestProf
|
|
68
70
|
end
|
69
71
|
|
70
72
|
log :info, msgs.join
|
73
|
+
|
74
|
+
stamp! if FactoryDoctor.stamp?
|
75
|
+
end
|
76
|
+
|
77
|
+
def stamp!
|
78
|
+
stamper = RSpecStamp::Stamper.new
|
79
|
+
|
80
|
+
examples = Hash.new { |h, k| h[k] = [] }
|
81
|
+
|
82
|
+
@example_groups.each_value do |bad_examples|
|
83
|
+
bad_examples.each do |example|
|
84
|
+
file, line = example.metadata[:location].split(":")
|
85
|
+
examples[file] << line.to_i
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
examples.each do |file, lines|
|
90
|
+
stamper.stamp_file(file, lines.uniq)
|
91
|
+
end
|
92
|
+
|
93
|
+
msgs = []
|
94
|
+
|
95
|
+
msgs <<
|
96
|
+
<<-MSG.strip_heredoc
|
97
|
+
RSpec Stamp results
|
98
|
+
|
99
|
+
Total patches: #{stamper.total}
|
100
|
+
Total files: #{examples.keys.size}
|
101
|
+
|
102
|
+
Failed patches: #{stamper.failed}
|
103
|
+
Ignored files: #{stamper.ignored}
|
104
|
+
MSG
|
105
|
+
|
106
|
+
log :info, msgs.join
|
71
107
|
end
|
72
108
|
|
73
109
|
private
|
@@ -1,16 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "test_prof/ext/string_strip_heredoc"
|
4
|
+
|
3
5
|
module TestProf::FactoryProf
|
4
6
|
module Printers
|
5
7
|
module Simple # :nodoc: all
|
6
8
|
class << self
|
7
9
|
include TestProf::Logging
|
10
|
+
using TestProf::StringStripHeredoc
|
8
11
|
|
9
12
|
def dump(result)
|
10
13
|
msgs = []
|
11
14
|
|
12
15
|
msgs <<
|
13
|
-
|
16
|
+
<<-MSG.strip_heredoc
|
14
17
|
Factories usage
|
15
18
|
|
16
19
|
total top-level name
|
@@ -12,7 +12,7 @@ RSpec.shared_context "any_fixture:clean", with_clean_fixture: true do
|
|
12
12
|
|
13
13
|
def open_transaction?
|
14
14
|
pool = ActiveRecord::Base.connection_pool
|
15
|
-
pool.active_connection? && pool.connection.open_transactions
|
15
|
+
pool.active_connection? && pool.connection.open_transactions > 0
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
@@ -22,7 +22,7 @@ module TestProf
|
|
22
22
|
# Use uniq prefix for instance variables to avoid collisions
|
23
23
|
# We want to use the power of Ruby's unicode support)
|
24
24
|
# And we love cats!)
|
25
|
-
PREFIX = "@😸"
|
25
|
+
PREFIX = RUBY_ENGINE == 'jruby' ? "@__jruby_is_not_cat_friendly__".freeze : "@😸".freeze
|
26
26
|
|
27
27
|
def let_it_be(identifier, **options, &block)
|
28
28
|
initializer = proc do
|
@@ -41,7 +41,13 @@ module TestProf
|
|
41
41
|
def define_let_it_be_methods(identifier, reload: false, refind: false)
|
42
42
|
let_accessor = -> { instance_variable_get(:"#{PREFIX}#{identifier}") }
|
43
43
|
|
44
|
-
|
44
|
+
if reload
|
45
|
+
let_accessor = lambda do
|
46
|
+
record = instance_variable_get(:"#{PREFIX}#{identifier}")
|
47
|
+
next unless record.is_a?(::ActiveRecord::Base)
|
48
|
+
record.reload
|
49
|
+
end
|
50
|
+
end
|
45
51
|
|
46
52
|
if refind
|
47
53
|
let_accessor = lambda do
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "test_prof/rspec_stamp"
|
3
4
|
require "test_prof/logging"
|
4
5
|
|
5
6
|
module TestProf
|
@@ -35,7 +36,14 @@ module TestProf
|
|
35
36
|
attr_accessor :top_count
|
36
37
|
|
37
38
|
def initialize
|
38
|
-
@top_count = (ENV['
|
39
|
+
@top_count = (ENV['RD_PROF_TOP'] || 5).to_i
|
40
|
+
@stamp = ENV['RD_PROF_STAMP']
|
41
|
+
|
42
|
+
RSpecStamp.config.tags = @stamp if stamp?
|
43
|
+
end
|
44
|
+
|
45
|
+
def stamp?
|
46
|
+
!@stamp.nil?
|
39
47
|
end
|
40
48
|
end
|
41
49
|
|
@@ -99,6 +107,6 @@ end
|
|
99
107
|
|
100
108
|
require "test_prof/rspec_dissect/rspec" if defined?(RSpec::Core)
|
101
109
|
|
102
|
-
TestProf.activate('
|
110
|
+
TestProf.activate('RD_PROF') do
|
103
111
|
TestProf::RSpecDissect.init
|
104
112
|
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
require "test_prof/ext/float_duration"
|
4
4
|
require "test_prof/ext/string_truncate"
|
5
5
|
require "test_prof/utils/sized_ordered_set"
|
6
|
+
require "test_prof/ext/string_strip_heredoc"
|
6
7
|
|
7
8
|
module TestProf
|
8
9
|
module RSpecDissect
|
@@ -10,6 +11,7 @@ module TestProf
|
|
10
11
|
include Logging
|
11
12
|
using FloatDuration
|
12
13
|
using StringTruncate
|
14
|
+
using StringStripHeredoc
|
13
15
|
|
14
16
|
NOTIFICATIONS = %i[
|
15
17
|
example_finished
|
@@ -58,7 +60,7 @@ module TestProf
|
|
58
60
|
msgs = []
|
59
61
|
|
60
62
|
msgs <<
|
61
|
-
|
63
|
+
<<-MSG.strip_heredoc
|
62
64
|
RSpecDissect report
|
63
65
|
|
64
66
|
Total time: #{@total_examples_time.duration}
|
@@ -68,32 +70,66 @@ module TestProf
|
|
68
70
|
MSG
|
69
71
|
|
70
72
|
msgs <<
|
71
|
-
|
73
|
+
<<-MSG.strip_heredoc
|
72
74
|
Top #{top_count} slowest suites (by `before(:each)` time):
|
73
75
|
|
74
76
|
MSG
|
75
77
|
|
76
78
|
@before_results.each do |group|
|
77
79
|
msgs <<
|
78
|
-
|
80
|
+
<<-GROUP.strip_heredoc
|
79
81
|
#{group[:desc].truncate} (#{group[:loc]}) – #{group[:before].duration} of #{group[:total].duration} (#{group[:count]})
|
80
82
|
GROUP
|
81
83
|
end
|
82
84
|
|
83
85
|
msgs <<
|
84
|
-
|
86
|
+
<<-MSG.strip_heredoc
|
87
|
+
|
85
88
|
Top #{top_count} slowest suites (by `let` time):
|
86
89
|
|
87
90
|
MSG
|
88
91
|
|
89
92
|
@memo_results.each do |group|
|
90
93
|
msgs <<
|
91
|
-
|
94
|
+
<<-GROUP.strip_heredoc
|
92
95
|
#{group[:desc].truncate} (#{group[:loc]}) – #{group[:memo].duration} of #{group[:total].duration} (#{group[:count]})
|
93
96
|
GROUP
|
94
97
|
end
|
95
98
|
|
96
99
|
log :info, msgs.join
|
100
|
+
|
101
|
+
stamp! if RSpecDissect.config.stamp?
|
102
|
+
end
|
103
|
+
|
104
|
+
def stamp!
|
105
|
+
stamper = RSpecStamp::Stamper.new
|
106
|
+
|
107
|
+
examples = Hash.new { |h, k| h[k] = [] }
|
108
|
+
|
109
|
+
(@before_results.to_a + @memo_results.to_a)
|
110
|
+
.map { |obj| obj[:loc] }.each do |location|
|
111
|
+
file, line = location.split(":")
|
112
|
+
examples[file] << line.to_i
|
113
|
+
end
|
114
|
+
|
115
|
+
examples.each do |file, lines|
|
116
|
+
stamper.stamp_file(file, lines.uniq)
|
117
|
+
end
|
118
|
+
|
119
|
+
msgs = []
|
120
|
+
|
121
|
+
msgs <<
|
122
|
+
<<-MSG.strip_heredoc
|
123
|
+
RSpec Stamp results
|
124
|
+
|
125
|
+
Total patches: #{stamper.total}
|
126
|
+
Total files: #{examples.keys.size}
|
127
|
+
|
128
|
+
Failed patches: #{stamper.failed}
|
129
|
+
Ignored files: #{stamper.ignored}
|
130
|
+
MSG
|
131
|
+
|
132
|
+
log :info, msgs.join
|
97
133
|
end
|
98
134
|
|
99
135
|
private
|
@@ -106,7 +142,7 @@ module TestProf
|
|
106
142
|
end
|
107
143
|
|
108
144
|
# Register RSpecDissect listener
|
109
|
-
TestProf.activate('
|
145
|
+
TestProf.activate('RD_PROF') do
|
110
146
|
RSpec.configure do |config|
|
111
147
|
listener = TestProf::RSpecDissect::Listener.new
|
112
148
|
|
@@ -10,7 +10,8 @@ module TestProf
|
|
10
10
|
|
11
11
|
# RSpecStamp configuration
|
12
12
|
class Configuration
|
13
|
-
|
13
|
+
attr_reader :tags
|
14
|
+
attr_accessor :ignore_files, :dry_run
|
14
15
|
|
15
16
|
def initialize
|
16
17
|
@ignore_files = [%r{spec/support}]
|
@@ -44,6 +45,49 @@ module TestProf
|
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
48
|
+
# Stamper collects statistics about applying tags
|
49
|
+
# to examples.
|
50
|
+
class Stamper
|
51
|
+
include TestProf::Logging
|
52
|
+
|
53
|
+
attr_reader :total, :failed, :ignored
|
54
|
+
|
55
|
+
def initialize
|
56
|
+
@total = 0
|
57
|
+
@failed = 0
|
58
|
+
@ignored = 0
|
59
|
+
end
|
60
|
+
|
61
|
+
def stamp_file(file, lines)
|
62
|
+
@total += lines.size
|
63
|
+
return if ignored?(file)
|
64
|
+
|
65
|
+
log :info, "(dry-run) Patching #{file}" if dry_run?
|
66
|
+
|
67
|
+
code = File.readlines(file)
|
68
|
+
|
69
|
+
@failed += RSpecStamp.apply_tags(code, lines, RSpecStamp.config.tags)
|
70
|
+
|
71
|
+
File.write(file, code.join) unless dry_run?
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def ignored?(file)
|
77
|
+
ignored = RSpecStamp.config.ignore_files.find do |pattern|
|
78
|
+
file =~ pattern
|
79
|
+
end
|
80
|
+
|
81
|
+
return unless ignored
|
82
|
+
log :warn, "Ignore stamping file: #{file}"
|
83
|
+
@ignored += 1
|
84
|
+
end
|
85
|
+
|
86
|
+
def dry_run?
|
87
|
+
RSpecStamp.config.dry_run?
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
47
91
|
class << self
|
48
92
|
include TestProf::Logging
|
49
93
|
|
@@ -84,11 +128,11 @@ module TestProf
|
|
84
128
|
parsed = Parser.parse(code)
|
85
129
|
return false unless parsed
|
86
130
|
|
87
|
-
parsed.desc
|
131
|
+
desc = parsed.desc_const || quote(parsed.desc || 'works')
|
88
132
|
|
89
133
|
tags.each do |t|
|
90
134
|
if t.is_a?(Hash)
|
91
|
-
t.
|
135
|
+
t.each_key do |k|
|
92
136
|
parsed.remove_tag(k)
|
93
137
|
parsed.add_htag(k, t[k])
|
94
138
|
end
|
@@ -101,9 +145,9 @@ module TestProf
|
|
101
145
|
need_parens = block == "{"
|
102
146
|
|
103
147
|
tags_str = parsed.tags.map { |t| t.is_a?(Symbol) ? ":#{t}" : t }.join(", ") unless
|
104
|
-
parsed.tags.nil?
|
148
|
+
parsed.tags.nil? || parsed.tags.empty?
|
105
149
|
|
106
|
-
unless parsed.htags.nil?
|
150
|
+
unless parsed.htags.nil? || parsed.htags.empty?
|
107
151
|
htags_str = parsed.htags.map do |(k, v)|
|
108
152
|
vstr = v.is_a?(Symbol) ? ":#{v}" : quote(v)
|
109
153
|
|
@@ -112,7 +156,7 @@ module TestProf
|
|
112
156
|
end
|
113
157
|
|
114
158
|
replacement = "\\1#{parsed.fname}#{need_parens ? '(' : ' '}"\
|
115
|
-
"#{[
|
159
|
+
"#{[desc, tags_str, htags_str].compact.join(', ')}"\
|
116
160
|
"#{need_parens ? ') ' : ' '}\\3"
|
117
161
|
|
118
162
|
if config.dry_run?
|
@@ -126,6 +170,7 @@ module TestProf
|
|
126
170
|
# rubocop: enable Metrics/PerceivedComplexity
|
127
171
|
|
128
172
|
def quote(str)
|
173
|
+
return str unless str.is_a?(String)
|
129
174
|
if str.include?("'")
|
130
175
|
"\"#{str}\""
|
131
176
|
else
|
@@ -2,15 +2,13 @@
|
|
2
2
|
|
3
3
|
require "ripper"
|
4
4
|
|
5
|
-
# rubocop: disable Metrics/CyclomaticComplexity
|
6
|
-
|
7
5
|
module TestProf
|
8
6
|
module RSpecStamp
|
9
7
|
# Parse examples headers
|
10
8
|
module Parser
|
11
9
|
# Contains the result of parsing
|
12
10
|
class Result
|
13
|
-
attr_accessor :fname, :desc
|
11
|
+
attr_accessor :fname, :desc, :desc_const
|
14
12
|
attr_reader :tags, :htags
|
15
13
|
|
16
14
|
def add_tag(v)
|
@@ -24,12 +22,14 @@ module TestProf
|
|
24
22
|
end
|
25
23
|
|
26
24
|
def remove_tag(tag)
|
27
|
-
@tags
|
28
|
-
@htags
|
25
|
+
@tags.delete(tag) if @tags
|
26
|
+
@htags.delete_if { |(k, _v)| k == tag } if @htags
|
29
27
|
end
|
30
28
|
end
|
31
29
|
|
32
30
|
class << self
|
31
|
+
# rubocop: disable Metrics/CyclomaticComplexity
|
32
|
+
# rubocop: disable Metrics/PerceivedComplexity
|
33
33
|
def parse(code)
|
34
34
|
sexp = Ripper.sexp(code)
|
35
35
|
return unless sexp
|
@@ -58,11 +58,17 @@ module TestProf
|
|
58
58
|
res = Result.new
|
59
59
|
|
60
60
|
fcall = sexp[1][0][1]
|
61
|
-
fcall = fcall[1] if fcall.first == :fcall
|
62
|
-
res.fname = fcall[1]
|
63
|
-
|
64
61
|
args_block = sexp[1][0][2]
|
65
62
|
|
63
|
+
if fcall.first == :fcall
|
64
|
+
fcall = fcall[1]
|
65
|
+
elsif fcall.first == :var_ref
|
66
|
+
res.fname = [parse_const(fcall), sexp[1][0][3][1]].join(".")
|
67
|
+
args_block = sexp[1][0][4]
|
68
|
+
end
|
69
|
+
|
70
|
+
res.fname ||= fcall[1]
|
71
|
+
|
66
72
|
return res if args_block.nil?
|
67
73
|
|
68
74
|
args_block = args_block[1] if args_block.first == :arg_paren
|
@@ -71,12 +77,16 @@ module TestProf
|
|
71
77
|
|
72
78
|
if args.first.first == :string_literal
|
73
79
|
res.desc = parse_literal(args.shift)
|
80
|
+
elsif args.first.first == :var_ref || args.first.first == :const_path_ref
|
81
|
+
res.desc_const = parse_const(args.shift)
|
74
82
|
end
|
75
83
|
|
76
84
|
parse_arg(res, args.shift) until args.empty?
|
77
85
|
|
78
86
|
res
|
79
87
|
end
|
88
|
+
# rubocop: enable Metrics/CyclomaticComplexity
|
89
|
+
# rubocop: enable Metrics/PerceivedComplexity
|
80
90
|
|
81
91
|
private
|
82
92
|
|
@@ -90,7 +100,24 @@ module TestProf
|
|
90
100
|
|
91
101
|
def parse_hash(res, hash_arg)
|
92
102
|
hash_arg.each do |(_, label, val)|
|
93
|
-
res.add_htag label[1][0..-2].to_sym,
|
103
|
+
res.add_htag label[1][0..-2].to_sym, parse_value(val)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Expr of the form:
|
108
|
+
# bool - [:var_ref, [:@kw, "true", [1, 24]]]
|
109
|
+
# string - [:string_literal, [:string_content, [...]]]
|
110
|
+
# int - [:@int, "3", [1, 52]]]]
|
111
|
+
def parse_value(expr)
|
112
|
+
case expr.first
|
113
|
+
when :var_ref
|
114
|
+
expr[1][1] == "true"
|
115
|
+
when :@int
|
116
|
+
expr[1].to_i
|
117
|
+
when :@float
|
118
|
+
expr[1].to_f
|
119
|
+
else
|
120
|
+
parse_literal(expr)
|
94
121
|
end
|
95
122
|
end
|
96
123
|
|
@@ -102,6 +129,25 @@ module TestProf
|
|
102
129
|
expr[0] == :assoc_new
|
103
130
|
val
|
104
131
|
end
|
132
|
+
|
133
|
+
# Expr of the form:
|
134
|
+
# [:var_ref, [:@const, "User", [1, 9]]]
|
135
|
+
#
|
136
|
+
# or
|
137
|
+
#
|
138
|
+
# [:const_path_ref, [:const_path_ref, [:var_ref,
|
139
|
+
# [:@const, "User", [1, 17]]],
|
140
|
+
# [:@const, "Guest", [1, 23]]],
|
141
|
+
# [:@const, "Collection", [1, 30]]
|
142
|
+
def parse_const(expr)
|
143
|
+
if expr.first == :var_ref
|
144
|
+
expr[1][1]
|
145
|
+
elsif expr.first == :@const
|
146
|
+
expr[1]
|
147
|
+
elsif expr.first == :const_path_ref
|
148
|
+
expr[1..-1].map(&method(:parse_const)).join("::")
|
149
|
+
end
|
150
|
+
end
|
105
151
|
end
|
106
152
|
end
|
107
153
|
end
|
@@ -1,9 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "test_prof/ext/string_strip_heredoc"
|
4
|
+
|
3
5
|
module TestProf
|
4
6
|
module RSpecStamp
|
5
7
|
class RSpecListener # :nodoc:
|
6
8
|
include Logging
|
9
|
+
using StringStripHeredoc
|
7
10
|
|
8
11
|
NOTIFICATIONS = %i[
|
9
12
|
example_failed
|
@@ -27,54 +30,27 @@ module TestProf
|
|
27
30
|
end
|
28
31
|
|
29
32
|
def stamp!
|
33
|
+
stamper = Stamper.new
|
34
|
+
|
30
35
|
@examples.each do |file, lines|
|
31
|
-
stamp_file(file, lines.uniq)
|
36
|
+
stamper.stamp_file(file, lines.uniq)
|
32
37
|
end
|
33
38
|
|
34
39
|
msgs = []
|
35
40
|
|
36
41
|
msgs <<
|
37
|
-
|
42
|
+
<<-MSG.strip_heredoc
|
38
43
|
RSpec Stamp results
|
39
44
|
|
40
|
-
Total patches: #{
|
45
|
+
Total patches: #{stamper.total}
|
41
46
|
Total files: #{@examples.keys.size}
|
42
47
|
|
43
|
-
Failed patches: #{
|
44
|
-
Ignored files: #{
|
48
|
+
Failed patches: #{stamper.failed}
|
49
|
+
Ignored files: #{stamper.ignored}
|
45
50
|
MSG
|
46
51
|
|
47
52
|
log :info, msgs.join
|
48
53
|
end
|
49
|
-
|
50
|
-
private
|
51
|
-
|
52
|
-
def stamp_file(file, lines)
|
53
|
-
@total += lines.size
|
54
|
-
return if ignored?(file)
|
55
|
-
|
56
|
-
log :info, "(dry-run) Patching #{file}" if dry_run?
|
57
|
-
|
58
|
-
code = File.readlines(file)
|
59
|
-
|
60
|
-
@failed += RSpecStamp.apply_tags(code, lines, RSpecStamp.config.tags)
|
61
|
-
|
62
|
-
File.write(file, code.join) unless dry_run?
|
63
|
-
end
|
64
|
-
|
65
|
-
def ignored?(file)
|
66
|
-
ignored = RSpecStamp.config.ignore_files.find do |pattern|
|
67
|
-
file =~ pattern
|
68
|
-
end
|
69
|
-
|
70
|
-
return unless ignored
|
71
|
-
log :warn, "Ignore stamping file: #{file}"
|
72
|
-
@ignored += 1
|
73
|
-
end
|
74
|
-
|
75
|
-
def dry_run?
|
76
|
-
RSpecStamp.config.dry_run?
|
77
|
-
end
|
78
54
|
end
|
79
55
|
end
|
80
56
|
end
|
data/lib/test_prof/ruby_prof.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "test_prof/ext/string_strip_heredoc"
|
4
|
+
|
3
5
|
module TestProf
|
4
6
|
# RubyProf wrapper.
|
5
7
|
#
|
@@ -48,7 +50,8 @@ module TestProf
|
|
48
50
|
:include_threads, :eliminate_methods
|
49
51
|
|
50
52
|
def initialize
|
51
|
-
@printer = ENV.
|
53
|
+
@printer = ENV['TEST_RUBY_PROF'].to_sym if PRINTERS.key?(ENV['TEST_RUBY_PROF'])
|
54
|
+
@printer ||= ENV.fetch('TEST_RUBY_PROF_PRINTER', :flat).to_sym
|
52
55
|
@mode = ENV.fetch('TEST_RUBY_PROF_MODE', :wall).to_sym
|
53
56
|
@min_percent = 1
|
54
57
|
@include_threads = false
|
@@ -119,6 +122,7 @@ module TestProf
|
|
119
122
|
|
120
123
|
class << self
|
121
124
|
include Logging
|
125
|
+
using StringStripHeredoc
|
122
126
|
|
123
127
|
def config
|
124
128
|
@config ||= Configuration.new
|
@@ -172,7 +176,7 @@ module TestProf
|
|
172
176
|
ENV["RUBY_PROF_MEASURE_MODE"] = config.mode.to_s
|
173
177
|
@initialized = TestProf.require(
|
174
178
|
'ruby-prof',
|
175
|
-
|
179
|
+
<<-MSG.strip_heredoc
|
176
180
|
Please, install 'ruby-prof' first:
|
177
181
|
# Gemfile
|
178
182
|
gem 'ruby-prof', '>= 0.16.0', require: false
|
@@ -184,7 +188,7 @@ module TestProf
|
|
184
188
|
if Utils.verify_gem_version('ruby-prof', at_least: '0.16.0')
|
185
189
|
true
|
186
190
|
else
|
187
|
-
log :error,
|
191
|
+
log :error, <<-MGS.strip_heredoc
|
188
192
|
Please, upgrade 'ruby-prof' to version >= 0.16.0.
|
189
193
|
MGS
|
190
194
|
false
|
data/lib/test_prof/stack_prof.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "test_prof/ext/string_strip_heredoc"
|
4
|
+
|
3
5
|
module TestProf
|
4
6
|
# StackProf wrapper.
|
5
7
|
#
|
@@ -26,12 +28,13 @@ module TestProf
|
|
26
28
|
|
27
29
|
def initialize
|
28
30
|
@mode = ENV.fetch('TEST_STACK_PROF_MODE', :wall).to_sym
|
29
|
-
@raw = ENV['TEST_STACK_PROF_RAW'] ==
|
31
|
+
@raw = ENV['TEST_STACK_PROF'] == 'raw' || ENV['TEST_STACK_PROF_RAW'] == 1
|
30
32
|
end
|
31
33
|
end
|
32
34
|
|
33
35
|
class << self
|
34
36
|
include Logging
|
37
|
+
using StringStripHeredoc
|
35
38
|
|
36
39
|
def config
|
37
40
|
@config ||= Configuration.new
|
@@ -88,7 +91,7 @@ module TestProf
|
|
88
91
|
|
89
92
|
html_path = path.gsub(/\.dump$/, '.html')
|
90
93
|
|
91
|
-
log :info,
|
94
|
+
log :info, <<-MSG.strip_heredoc
|
92
95
|
Run the following command to generate a flame graph report:
|
93
96
|
|
94
97
|
stackprof --flamegraph #{path} > #{html_path} && stackprof --flamegraph-viewer=#{html_path}
|
@@ -111,7 +114,7 @@ module TestProf
|
|
111
114
|
return @initialized if instance_variable_defined?(:@initialized)
|
112
115
|
@initialized = TestProf.require(
|
113
116
|
'stackprof',
|
114
|
-
|
117
|
+
<<-MSG.strip_heredoc
|
115
118
|
Please, install 'stackprof' first:
|
116
119
|
# Gemfile
|
117
120
|
gem 'stackprof', '>= 0.2.9', require: false
|
@@ -123,9 +126,9 @@ module TestProf
|
|
123
126
|
if Utils.verify_gem_version('stackprof', at_least: '0.2.9')
|
124
127
|
true
|
125
128
|
else
|
126
|
-
log :error,
|
129
|
+
log :error, <<-MSG.strip_heredoc
|
127
130
|
Please, upgrade 'stackprof' to version >= 0.2.9.
|
128
|
-
|
131
|
+
MSG
|
129
132
|
false
|
130
133
|
end
|
131
134
|
end
|
@@ -1,12 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "test_prof/ext/float_duration"
|
4
|
+
require "test_prof/ext/string_strip_heredoc"
|
4
5
|
|
5
6
|
module TestProf
|
6
7
|
module TagProf
|
7
8
|
class RSpecListener # :nodoc:
|
8
9
|
include Logging
|
9
10
|
using FloatDuration
|
11
|
+
using StringStripHeredoc
|
10
12
|
|
11
13
|
NOTIFICATIONS = %i[
|
12
14
|
example_started
|
@@ -37,7 +39,7 @@ module TestProf
|
|
37
39
|
msgs = []
|
38
40
|
|
39
41
|
msgs <<
|
40
|
-
|
42
|
+
<<-MSG.strip_heredoc
|
41
43
|
TagProf report for #{@tag}
|
42
44
|
MSG
|
43
45
|
|
@@ -4,6 +4,11 @@ module TestProf
|
|
4
4
|
module Utils
|
5
5
|
# Ordered set with capacity
|
6
6
|
class SizedOrderedSet
|
7
|
+
unless [].respond_to?(:bsearch_index)
|
8
|
+
require "test_prof/ext/array_bsearch_index"
|
9
|
+
using ArrayBSearchIndex
|
10
|
+
end
|
11
|
+
|
7
12
|
include Enumerable
|
8
13
|
|
9
14
|
def initialize(max_size, sort_by: nil)
|
@@ -49,7 +54,7 @@ module TestProf
|
|
49
54
|
end
|
50
55
|
|
51
56
|
def to_a
|
52
|
-
data
|
57
|
+
data.dup
|
53
58
|
end
|
54
59
|
|
55
60
|
private
|
data/lib/test_prof/version.rb
CHANGED
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.3.0
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vladimir Dementyev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-09-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -72,28 +72,14 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '0.
|
75
|
+
version: '0.50'
|
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.
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: pry-byebug
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
82
|
+
version: '0.50'
|
97
83
|
description: "\n Ruby applications tests profiling tools.\n\n Contains tools
|
98
84
|
to anylyze factories usage, integrate with Ruby profilers,\n profile your examples
|
99
85
|
using ActiveSupport notifications (if any) and\n statically analyze your code
|
@@ -142,7 +128,9 @@ files:
|
|
142
128
|
- lib/test_prof/event_prof/instrumentations/active_support.rb
|
143
129
|
- lib/test_prof/event_prof/minitest.rb
|
144
130
|
- lib/test_prof/event_prof/rspec.rb
|
131
|
+
- lib/test_prof/ext/array_bsearch_index.rb
|
145
132
|
- lib/test_prof/ext/float_duration.rb
|
133
|
+
- lib/test_prof/ext/string_strip_heredoc.rb
|
146
134
|
- lib/test_prof/ext/string_truncate.rb
|
147
135
|
- lib/test_prof/factory_default.rb
|
148
136
|
- lib/test_prof/factory_default/factory_girl_patch.rb
|
@@ -188,12 +176,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
188
176
|
requirements:
|
189
177
|
- - ">="
|
190
178
|
- !ruby/object:Gem::Version
|
191
|
-
version: 2.
|
179
|
+
version: 2.2.0
|
192
180
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
193
181
|
requirements:
|
194
|
-
- - "
|
182
|
+
- - ">="
|
195
183
|
- !ruby/object:Gem::Version
|
196
|
-
version:
|
184
|
+
version: '0'
|
197
185
|
requirements: []
|
198
186
|
rubyforge_project:
|
199
187
|
rubygems_version: 2.6.11
|