test-prof 0.2.1 → 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 +59 -1
- data/README.md +12 -2
- data/guides/event_prof.md +12 -0
- data/guides/factory_doctor.md +10 -0
- data/guides/let_it_be.md +96 -0
- data/guides/rspec_dissect.md +58 -0
- data/guides/ruby_prof.md +5 -5
- data/guides/stack_prof.md +3 -3
- data/lib/test_prof/cops/rspec/aggregate_failures.rb +2 -2
- 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 +44 -5
- data/lib/test_prof/event_prof.rb +38 -25
- data/lib/test_prof/ext/array_bsearch_index.rb +15 -0
- data/lib/test_prof/ext/float_duration.rb +0 -1
- data/lib/test_prof/ext/string_strip_heredoc.rb +15 -0
- data/lib/test_prof/ext/string_truncate.rb +19 -0
- data/lib/test_prof/factory_doctor/rspec.rb +38 -2
- data/lib/test_prof/factory_doctor.rb +12 -4
- 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/before_all.rb +8 -0
- data/lib/test_prof/recipes/rspec/let_it_be.rb +80 -0
- data/lib/test_prof/rspec_dissect/rspec.rb +157 -0
- data/lib/test_prof/rspec_dissect.rb +112 -0
- data/lib/test_prof/rspec_stamp/parser.rb +58 -7
- data/lib/test_prof/rspec_stamp/rspec.rb +10 -34
- data/lib/test_prof/rspec_stamp.rb +55 -6
- data/lib/test_prof/ruby_prof.rb +19 -4
- data/lib/test_prof/stack_prof.rb +19 -5
- data/lib/test_prof/tag_prof/rspec.rb +5 -3
- data/lib/test_prof/utils/sized_ordered_set.rb +65 -0
- data/lib/test_prof/utils.rb +18 -0
- data/lib/test_prof/version.rb +1 -1
- data/lib/test_prof.rb +15 -2
- metadata +15 -75
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
|
@@ -1,6 +1,64 @@
|
|
|
1
1
|
# Change log
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
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.
|
|
20
|
+
|
|
21
|
+
- [#14](https://github.com/palkan/test-prof/pull/14) RSpecDissect profiler. ([@palkan][])
|
|
22
|
+
|
|
23
|
+
RSpecDissect tracks how much time do you spend in `before` hooks
|
|
24
|
+
and memoization helpers (i.e. `let`) in your tests.
|
|
25
|
+
|
|
26
|
+
- [#13](https://github.com/palkan/test-prof/pull/13) RSpec `let_it_be` method. ([@palkan][])
|
|
27
|
+
|
|
28
|
+
Just like `let`, but persist the result for the whole group (i.e. `let` + `before_all`).
|
|
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
|
+
|
|
50
|
+
## 0.2.3
|
|
51
|
+
|
|
52
|
+
- Minor improvements. ([@palkan][])
|
|
53
|
+
|
|
54
|
+
## 0.2.2
|
|
55
|
+
|
|
56
|
+
- Fix time calculation when Time class is monkey-patched. ([@palkan][])
|
|
57
|
+
|
|
58
|
+
Add `TestProf.now` method which is just a copy of original `Time.now` and
|
|
59
|
+
use it everywhere.
|
|
60
|
+
|
|
61
|
+
Fixes [#10](https://github.com/palkan/test-prof/issues/10).
|
|
4
62
|
|
|
5
63
|
## 0.2.1
|
|
6
64
|
|
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
|
|
|
@@ -63,6 +69,8 @@ Checkout our guides for each specific tool:
|
|
|
63
69
|
|
|
64
70
|
- [Factory Profiler](https://github.com/palkan/test-prof/tree/master/guides/factory_prof.md)
|
|
65
71
|
|
|
72
|
+
- [RSpecDissect Profiler](https://github.com/palkan/test-prof/tree/master/guides/rspec_dissect.md)
|
|
73
|
+
|
|
66
74
|
- [Rubocop Cops](https://github.com/palkan/test-prof/tree/master/guides/rubocop.md)
|
|
67
75
|
|
|
68
76
|
## Tips and Tricks (or _Recipes_)
|
|
@@ -71,6 +79,8 @@ We also want to share some small code tricks which can help you to improve your
|
|
|
71
79
|
|
|
72
80
|
- [`before_all` Hook](https://github.com/palkan/test-prof/tree/master/guides/before_all.md)
|
|
73
81
|
|
|
82
|
+
- [`let_it_be` Helper](https://github.com/palkan/test-prof/tree/master/guides/let_it_be.md)
|
|
83
|
+
|
|
74
84
|
- [AnyFixture](https://github.com/palkan/test-prof/tree/master/guides/any_fixture.md)
|
|
75
85
|
|
|
76
86
|
- [FactoryDefault](https://github.com/palkan/test-prof/tree/master/guides/factory_default.md)
|
data/guides/event_prof.md
CHANGED
|
@@ -55,6 +55,8 @@ TestProf::EventProf.configure do |config|
|
|
|
55
55
|
end
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
+
Or provide the `EVENT_PROF_EXAMPLES=1` env variable.
|
|
59
|
+
|
|
58
60
|
Another useful configuration parameter – `rank_by`. It's responsible for sorting stats –
|
|
59
61
|
either by the time spent in the event or by the number of occurrences:
|
|
60
62
|
|
|
@@ -64,6 +66,16 @@ EVENT_PROF_RANK=count EVENT_PROF='instantiation.active_record' be rspec
|
|
|
64
66
|
|
|
65
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.
|
|
66
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
|
+
|
|
67
79
|
## Custom Instrumentation
|
|
68
80
|
|
|
69
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/let_it_be.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Let It Be!
|
|
2
|
+
|
|
3
|
+
Let's bring a little bit of magic and introduce a new way to setup a _shared_ test data.
|
|
4
|
+
|
|
5
|
+
Suppose you have the following setup:
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
describe BeatleWeightedSearchQuery do
|
|
9
|
+
let!(:paul) { create(:beatle, name: 'Paul') }
|
|
10
|
+
let!(:ringo) { create(:beatle, name: 'Ringo') }
|
|
11
|
+
let!(:george) { create(:beatle, name: 'George') }
|
|
12
|
+
let!(:john) { create(:beatle, name: 'John') }
|
|
13
|
+
|
|
14
|
+
specify { expect(subject.call('joh')).to contain_exactly(john) }
|
|
15
|
+
|
|
16
|
+
# and more examples here
|
|
17
|
+
end
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
We don't need to re-create the Fab Four for every example, do we?
|
|
21
|
+
|
|
22
|
+
We already have [`before_all`](https://github.com/palkan/test-prof/tree/master/guides/before_all.md) to solve the problem of _repeatable_ data:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
describe BeatleWeightedSearchQuery do
|
|
26
|
+
before_all do
|
|
27
|
+
@paul = create(:beatle, name: 'Paul')
|
|
28
|
+
...
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
specify { expect(subject.call('joh')).to contain_exactly(@john) }
|
|
32
|
+
|
|
33
|
+
...
|
|
34
|
+
end
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
That technique works pretty good but requires us to use instance variables and define everything at once. Thus it's not easy to refactor existing tests which use `let/let!` instead.
|
|
38
|
+
|
|
39
|
+
With `let_it_be` you can do the following:
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
describe BeatleWeightedSearchQuery do
|
|
43
|
+
let_it_be(:paul) { create(:beatle, name: 'Paul') }
|
|
44
|
+
let_it_be(:ringo) { create(:beatle, name: 'Ringo') }
|
|
45
|
+
let_it_be(:george) { create(:beatle, name: 'George') }
|
|
46
|
+
let_it_be(:john) { create(:beatle, name: 'John') }
|
|
47
|
+
|
|
48
|
+
specify { expect(subject.call('joh')).to contain_exactly(john) }
|
|
49
|
+
|
|
50
|
+
# and more examples here
|
|
51
|
+
end
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
That's it! Just replace `let!` with `let_it_be`. That's equal to the `before_all` approach but requires less refactoring.
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
## Instructions
|
|
58
|
+
|
|
59
|
+
In your `spec_helper.rb`:
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
require "test_prof/recipes/rspec/let_it_be"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
In your tests:
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
describe MySuperDryService do
|
|
69
|
+
let_it_be(:user) { create(:user) }
|
|
70
|
+
|
|
71
|
+
...
|
|
72
|
+
end
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Caveats
|
|
76
|
+
|
|
77
|
+
If you modify objects generated within a `let_it_be` block in your examples, you maybe have to re-initiate them.
|
|
78
|
+
We have a built-in support for that:
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
# Use reload: true option to reload user object (assuming it's an instance of ActiveRecord)
|
|
83
|
+
# for every example
|
|
84
|
+
let_it_be(:user, reload: true) { create(:user) }
|
|
85
|
+
|
|
86
|
+
# it is almost equal to
|
|
87
|
+
before_all { @user = create(:user) }
|
|
88
|
+
let(:user) { @user.reload }
|
|
89
|
+
|
|
90
|
+
# You can also specify refind: true option to hard-reload the record
|
|
91
|
+
let_it_be(:user, refind: true) { create(:user) }
|
|
92
|
+
|
|
93
|
+
# it is almost equal to
|
|
94
|
+
before_all { @user = create(:user) }
|
|
95
|
+
let(:user) { User.find(@user.id) }
|
|
96
|
+
```
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# RSpecDissect
|
|
2
|
+
|
|
3
|
+
Do you know how much time you spend in `before` hooks? Or memoization helpers such as `let`? Usually, the most of the whole test suite time.
|
|
4
|
+
|
|
5
|
+
_RSpecDissect_ provides this kind of information and also shows you the worst example groups. The main purpose of RSpecDissect is to identify these slow groups and refactor them using [`before_all`](https://github.com/palkan/test-prof/tree/master/guides/before_all.md) or [`let_it_be`](https://github.com/palkan/test-prof/tree/master/guides/let_it_be.md) recipes.
|
|
6
|
+
|
|
7
|
+
Example output:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
[TEST PROF INFO] RSpecDissect enabled
|
|
11
|
+
|
|
12
|
+
Total time: 25:14.870
|
|
13
|
+
Total `before(:each)` time: 14:36.482
|
|
14
|
+
Total `let` time: 19:20.259
|
|
15
|
+
|
|
16
|
+
Top 5 slowest suites (by `before(:each)` time):
|
|
17
|
+
|
|
18
|
+
Webhooks::DispatchTransition (./spec/services/webhooks/dispatch_transition_spec.rb:3) – 00:29.895 of 00:33.706 (327)
|
|
19
|
+
FunnelsController (./spec/controllers/funnels_controller_spec.rb:3) – 00:22.117 of 00:43.649 (133)
|
|
20
|
+
ApplicantsController (./spec/controllers/applicants_controller_spec.rb:3) – 00:21.220 of 00:41.407 (222)
|
|
21
|
+
BookedSlotsController (./spec/controllers/booked_slots_controller_spec.rb:3) – 00:15.729 of 00:27.893 (50)
|
|
22
|
+
Analytics::Wor...rsion::Summary (./spec/services/analytics/workflow_conversion/summary_spec.rb:3) – 00:15.383 of 00:15.914 (12)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
Top 5 slowest suites (by `let` time):
|
|
26
|
+
|
|
27
|
+
FunnelsController (./spec/controllers/funnels_controller_spec.rb:3) – 00:38.532 of 00:43.649 (133)
|
|
28
|
+
ApplicantsController (./spec/controllers/applicants_controller_spec.rb:3) – 00:33.252 of 00:41.407 (222)
|
|
29
|
+
Webhooks::DispatchTransition (./spec/services/webhooks/dispatch_transition_spec.rb:3) – 00:30.320 of 00:33.706 (327)
|
|
30
|
+
BookedSlotsController (./spec/controllers/booked_slots_controller_spec.rb:3) – 00:25.710 of 00:27.893 (50)
|
|
31
|
+
AvailableSlotsController (./spec/controllers/available_slots_controller_spec.rb:3) – 00:18.481 of 00:23.366 (85)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Instructions
|
|
35
|
+
|
|
36
|
+
RSpecDissect can only be used with RSpec (which is clear from the name).
|
|
37
|
+
|
|
38
|
+
To activate RSpecDissect use `RD_PROF` environment variable:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
RD_PROF=1 rspec ...
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
You can also specify the number of top slow groups through `RD_PROF_TOP` variable:
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
RD_PROF=1 RD_PROF_TOP=10 rspec ...
|
|
48
|
+
```
|
|
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
|
@@ -9,7 +9,7 @@ Install `ruby-prof` gem (>= 0.16):
|
|
|
9
9
|
```ruby
|
|
10
10
|
# Gemfile
|
|
11
11
|
group :development, :test do
|
|
12
|
-
gem 'ruby-prof', require: false
|
|
12
|
+
gem 'ruby-prof', '>= 0.16.0', require: false
|
|
13
13
|
end
|
|
14
14
|
```
|
|
15
15
|
|
|
@@ -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
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
## Instructions
|
|
6
6
|
|
|
7
|
-
Install 'stackprof' gem (>= 0.2.
|
|
7
|
+
Install 'stackprof' gem (>= 0.2.9):
|
|
8
8
|
|
|
9
9
|
```ruby
|
|
10
10
|
# Gemfile
|
|
11
11
|
group :development, :test do
|
|
12
|
-
gem 'stackprof', require: false
|
|
12
|
+
gem 'stackprof', '>= 0.2.9', require: false
|
|
13
13
|
end
|
|
14
14
|
```
|
|
15
15
|
|
|
@@ -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
|
|
@@ -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.
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "test_prof/ext/float_duration"
|
|
4
|
+
require "test_prof/ext/string_truncate"
|
|
5
|
+
require "test_prof/ext/string_strip_heredoc"
|
|
4
6
|
|
|
5
7
|
module TestProf
|
|
6
8
|
module EventProf
|
|
7
9
|
class RSpecListener # :nodoc:
|
|
8
10
|
include Logging
|
|
9
11
|
using FloatDuration
|
|
12
|
+
using StringTruncate
|
|
13
|
+
using StringStripHeredoc
|
|
10
14
|
|
|
11
15
|
NOTIFICATIONS = %i[
|
|
12
16
|
example_group_started
|
|
@@ -43,7 +47,7 @@ module TestProf
|
|
|
43
47
|
msgs = []
|
|
44
48
|
|
|
45
49
|
msgs <<
|
|
46
|
-
|
|
50
|
+
<<-MSG.strip_heredoc
|
|
47
51
|
EventProf results for #{@profiler.event}
|
|
48
52
|
|
|
49
53
|
Total time: #{@profiler.total_time.duration}
|
|
@@ -58,8 +62,8 @@ module TestProf
|
|
|
58
62
|
location = group[:id].metadata[:location]
|
|
59
63
|
|
|
60
64
|
msgs <<
|
|
61
|
-
|
|
62
|
-
#{description} (#{location}) – #{group[:time].duration} (#{group[:count]} / #{group[:examples]})
|
|
65
|
+
<<-GROUP.strip_heredoc
|
|
66
|
+
#{description.truncate} (#{location}) – #{group[:time].duration} (#{group[:count]} / #{group[:examples]})
|
|
63
67
|
GROUP
|
|
64
68
|
end
|
|
65
69
|
|
|
@@ -70,13 +74,48 @@ module TestProf
|
|
|
70
74
|
description = example[:id].description
|
|
71
75
|
location = example[:id].metadata[:location]
|
|
72
76
|
msgs <<
|
|
73
|
-
|
|
74
|
-
#{description} (#{location}) – #{example[:time].duration} (#{example[:count]})
|
|
77
|
+
<<-GROUP.strip_heredoc
|
|
78
|
+
#{description.truncate} (#{location}) – #{example[:time].duration} (#{example[:count]})
|
|
75
79
|
GROUP
|
|
76
80
|
end
|
|
77
81
|
end
|
|
78
82
|
|
|
79
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
|
|
80
119
|
end
|
|
81
120
|
end
|
|
82
121
|
end
|
data/lib/test_prof/event_prof.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "test_prof/rspec_stamp"
|
|
3
4
|
require "test_prof/event_prof/instrumentations/active_support"
|
|
5
|
+
require "test_prof/utils/sized_ordered_set"
|
|
4
6
|
|
|
5
7
|
module TestProf
|
|
6
8
|
# EventProf profiles your tests and suites against custom events,
|
|
@@ -19,6 +21,8 @@ module TestProf
|
|
|
19
21
|
# TestProf::EventProf.configure do |config|
|
|
20
22
|
# config.per_example = true
|
|
21
23
|
# end
|
|
24
|
+
#
|
|
25
|
+
# Or provide the EVENT_PROF_EXAMPLES=1 env variable.
|
|
22
26
|
module EventProf
|
|
23
27
|
# EventProf configuration
|
|
24
28
|
class Configuration
|
|
@@ -34,8 +38,15 @@ module TestProf
|
|
|
34
38
|
@event = ENV['EVENT_PROF']
|
|
35
39
|
@instrumenter = :active_support
|
|
36
40
|
@top_count = (ENV['EVENT_PROF_TOP'] || 5).to_i
|
|
37
|
-
@per_example =
|
|
41
|
+
@per_example = ENV['EVENT_PROF_EXAMPLES'] == '1'
|
|
38
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?
|
|
39
50
|
end
|
|
40
51
|
|
|
41
52
|
def per_example?
|
|
@@ -73,7 +84,7 @@ module TestProf
|
|
|
73
84
|
class Profiler # :nodoc:
|
|
74
85
|
include TestProf::Logging
|
|
75
86
|
|
|
76
|
-
attr_reader :event, :
|
|
87
|
+
attr_reader :event, :total_count, :total_time
|
|
77
88
|
|
|
78
89
|
def initialize(event:, instrumenter:)
|
|
79
90
|
@event = event
|
|
@@ -82,13 +93,16 @@ module TestProf
|
|
|
82
93
|
|
|
83
94
|
instrumenter.subscribe(event) { |time| track(time) }
|
|
84
95
|
|
|
85
|
-
@groups =
|
|
86
|
-
|
|
96
|
+
@groups = Utils::SizedOrderedSet.new(
|
|
97
|
+
top_count, sort_by: rank_by
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
@examples = Utils::SizedOrderedSet.new(
|
|
101
|
+
top_count, sort_by: rank_by
|
|
102
|
+
)
|
|
87
103
|
|
|
88
104
|
@total_count = 0
|
|
89
105
|
@total_time = 0.0
|
|
90
|
-
|
|
91
|
-
reset!
|
|
92
106
|
end
|
|
93
107
|
|
|
94
108
|
def track(time)
|
|
@@ -99,41 +113,47 @@ module TestProf
|
|
|
99
113
|
@time += time
|
|
100
114
|
@count += 1
|
|
101
115
|
|
|
102
|
-
|
|
103
|
-
|
|
116
|
+
return if @current_example.nil?
|
|
117
|
+
|
|
118
|
+
@example_time += time
|
|
119
|
+
@example_count += 1
|
|
104
120
|
end
|
|
105
121
|
|
|
106
122
|
def group_started(id)
|
|
107
|
-
|
|
123
|
+
reset_group!
|
|
108
124
|
@current_group = id
|
|
109
125
|
end
|
|
110
126
|
|
|
111
127
|
def group_finished(id)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
@groups[
|
|
128
|
+
data = { id: id, time: @time, count: @count, examples: @total_examples }
|
|
129
|
+
|
|
130
|
+
@groups << data unless data[rank_by].zero?
|
|
131
|
+
|
|
115
132
|
@current_group = nil
|
|
116
133
|
end
|
|
117
134
|
|
|
118
|
-
def example_started(
|
|
135
|
+
def example_started(id)
|
|
136
|
+
return unless config.per_example?
|
|
119
137
|
reset_example!
|
|
138
|
+
@current_example = id
|
|
120
139
|
end
|
|
121
140
|
|
|
122
141
|
def example_finished(id)
|
|
123
142
|
@total_examples += 1
|
|
124
143
|
return unless config.per_example?
|
|
125
144
|
|
|
126
|
-
|
|
127
|
-
@examples[
|
|
145
|
+
data = { id: id, time: @example_time, count: @example_count }
|
|
146
|
+
@examples << data unless data[rank_by].zero?
|
|
147
|
+
@current_example = nil
|
|
128
148
|
end
|
|
129
149
|
|
|
130
150
|
def results
|
|
131
151
|
{
|
|
132
|
-
groups:
|
|
152
|
+
groups: @groups.to_a
|
|
133
153
|
}.tap do |data|
|
|
134
154
|
next unless config.per_example?
|
|
135
155
|
|
|
136
|
-
data[:examples] =
|
|
156
|
+
data[:examples] = @examples.to_a
|
|
137
157
|
end
|
|
138
158
|
end
|
|
139
159
|
|
|
@@ -147,21 +167,14 @@ module TestProf
|
|
|
147
167
|
|
|
148
168
|
private
|
|
149
169
|
|
|
150
|
-
def fetch_top(arr)
|
|
151
|
-
arr.reject { |el| el[rank_by].zero? }
|
|
152
|
-
.sort_by { |el| -el[rank_by] }
|
|
153
|
-
.take(top_count)
|
|
154
|
-
end
|
|
155
|
-
|
|
156
170
|
def config
|
|
157
171
|
EventProf.config
|
|
158
172
|
end
|
|
159
173
|
|
|
160
|
-
def
|
|
174
|
+
def reset_group!
|
|
161
175
|
@time = 0.0
|
|
162
176
|
@count = 0
|
|
163
177
|
@total_examples = 0
|
|
164
|
-
reset_example!
|
|
165
178
|
end
|
|
166
179
|
|
|
167
180
|
def reset_example!
|